# The 5 React Hooks you'll ever need

In recent years, React's ecosystem has witnessed a transformation, and it's largely thanks to Hooks. With them, stateful logic has never been more reusable and React code has never looked cleaner. If you're curious about why and how, let's embark on this enlightening journey through the world of React Hooks.

## **Intro to React Hooks**

Once upon a time, state and lifecycle features were exclusive to class components in React. That changed with Hooks. Suddenly, these capabilities were available to functional components, and the community's excitement was palpable. Beyond just offering enhanced features, Hooks championed a philosophy of cleaner and more intuitive design patterns.

## **The Evolution: Functional vs. Class-Based Components**

Before Hooks, the React community was sharply divided between two camps: the **Functional Components** and the **Class-Based Components**.

Functional components, known for their simplicity, were mainly used for presentational needs. They had less boilerplate, were considered efficient but lacked the ability to handle state or lifecycle events.

On the other side, class components came with their robustness. They could manage state, handle lifecycle events, and support `props` with ease. However, they also carried the baggage of extra boilerplate and sometimes convoluted logic, especially for newcomers.

## **1\. useState: Simplifying State Management**

State management, once a daunting task requiring a constructor and `this.state`, was simplified by the `useState` hook.

```javascript
import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }
  
  return <button onClick={handleClick}>You pressed me {count} times</button>;
}
```

With `useState`, we not only have a more concise way to manage state, but it also eliminates the need to bind `this` to functions, further simplifying function components.

## **2\. useEffect: Side Effects Made Easy**

If `useState` revolutionized state, `useEffect` did the same for side effects. This hook can mimic componentDidMount, componentDidUpdate, and componentWillUnmount lifecycle methods, making it a powerful tool in a developer's arsenal.

**2.1. Updating the Document Title**

One of the simplest uses of `useEffect` is to update the document title based on a state change, giving users a dynamic experience.

```javascript
import { useState, useEffect } from "react";

function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]); // This effect runs whenever 'count' changes
}
```

This code updates the browser's tab title every time the `count` state changes, demonstrating how `useEffect` can be used for side effects related to state changes.

**2.2. Fetching Data with useEffect**

`useEffect` is frequently used for data fetching, allowing functional components to retrieve data right after they're mounted.

```javascript
import { useState, useEffect } from "react";

function FetchData() {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchData() {
      const response = await fetch("https://api.example.com/data");
      const result = await response.json();
      setData(result);
      setLoading(false);
    }

    fetchData();
  }, []); // Empty dependency array means this useEffect runs once after component mounts
}
```

## **3\. useContext: Managing Global State with Elegance**

When working on larger React applications, managing state and passing data through multiple layers of components can get cumbersome. This phenomenon, often referred to as "prop drilling," can make your codebase harder to maintain. The `useContext` hook in React aims to solve this problem by allowing you to create global state that can be accessed and modified from anywhere within your component tree, without having to pass props down manually.

### **3.1. How useContext Works**

At its core, `useContext` is a combination of two main parts:

1. **React.createContext**: This creates a new context. It returns an object with two values, `Provider` and `Consumer`. While you'd mainly use the `Provider` in conjunction with `useContext`, there are still scenarios where the `Consumer` might come in handy, especially in class components.
    
2. **useContext Hook**: This allows your functional components to tap into the provided context without having to wrap the component in a `Consumer`.
    

### **3.2. Practical Example**

Let's see `useContext` in action with a theme toggler for our application.

```javascript
import React, { useContext, useState } from 'react';

// 1. Create a new context
const ThemeContext = React.createContext();

function App() {
  const [theme, setTheme] = useState('light');

  // The value prop on Provider will provide these values to all children that tap into this context
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <Navbar />
      <Content />
    </ThemeContext.Provider>
  );
}

function Navbar() {
  const { theme, setTheme } = useContext(ThemeContext); // Accessing our context

  return (
    <nav>
      Current theme: {theme}
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
        Toggle Theme
      </button>
    </nav>
  );
}

function Content() {
  const { theme } = useContext(ThemeContext); // Accessing our context

  return (
    <div style={{ background: theme === 'light' ? '#eee' : '#333', color: theme === 'light' ? '#333' : '#eee' }}>
      This is our main content.
    </div>
  );
}
```

In the example above, we've created a context for theme management. The `Navbar` component can toggle the theme, and the `Content` component reacts to the theme change. All of this is achieved without having to pass the `theme` or `setTheme` function manually as props, showcasing the elegance and power of `useContext`.

## **4\. useReducer: Tackling Complex State Logic**

State management can sometimes get complex, especially when dealing with intertwined actions that can mutate our state in various ways. This is where `useReducer` shines. Inspired by the Redux pattern, `useReducer` provides a structured way to manage more intricate state logic in our React applications.

### **4.1. The Core Concept**

`useReducer` is fundamentally about using the reducer pattern within a component. A reducer is a function that determines changes to an application's state. It uses the current state and returns a new state.

Typically, with `useReducer`, you define an initial state and a reducer function, which specifies how the state changes in response to different actions.

### **4.2. Practical Example**

Let's explore `useReducer` with a counter that can both increase and decrease its value.

```javascript
import React, { useReducer } from 'react';

// Reducer function
function counterReducer(state, action) {
  switch(action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      throw new Error("Unsupported action type!");
  }
}

// Initial state
const initialState = { count: 0 };

function CounterApp() {
  const [state, dispatch] = useReducer(counterReducer, initialState);

  return (
    <div>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
    </div>
  );
}
```

In the example, we define a reducer function (`counterReducer`) and an initial state (`initialState`). The `CounterApp` component then uses `useReducer` to provide state management.

The reducer listens to dispatched actions. When we click the buttons, we dispatch actions (`INCREMENT` or `DECREMENT`) to the reducer. The reducer, in turn, decides how to update the state based on these actions.

What's more, the power of `useReducer` becomes truly evident in more complex state structures, where multiple intertwined actions can influence state changes.

## **5\. useMemo: Boosting Performance**

Performance optimization is crucial. With `useMemo`, developers can avoid unnecessary re-renders by memorizing computed values, leading to efficient resource utilization.

```javascript
import { useMemo } from "react";

const expensiveValue = useMemo(() => {
  // Some expensive computation here
}, [dependencies]);
```

## **The Power of Custom Hooks**

While React provides an impressive array of built-in hooks, it also grants developers the liberty to craft their custom hooks. These hooks encapsulate logic, making it shareable across components.

For instance, a `usePost` hook could simplify data fetching, abstracting the logic and making it reusable across your application.

## **Embracing the Future with Hooks**

With Hooks, the future beckons. They promote:

* A clear shift towards functional components.
    
* Enhanced code reusability.
    
* Optimized performance and leaner code structures.
    
* Intuitive design patterns and better separation of concerns.
    

React Hooks are more than just a feature; they're a testament to React's commitment to innovation, always ensuring developers have the best tools at hand. As we continue to build, let's harness the power of Hooks to create intuitive, maintainable, and scalable applications.

Interested in a visual walkthrough? [Click here](#) to watch my tech talk on React Hooks.
