Mastering React Hooks: A Comprehensive Guide with Examples
Tags: react, reacthooks, reactjsdevelopment, webdev
Published: 2024-05-11 10:16:29

**Introduction:**
React Hooks have revolutionized how developers write React components by allowing them to use state and other React features without writing a class. With the introduction of Hooks in React 16.8, developers gained a powerful toolset to manage stateful logic and side effects in functional components. In this blog post, we'll delve into the all-important React Hooks, explaining each in detail with practical examples.

**useState():**
`useState()` is the most fundamental Hook in React. It allows functional components to manage the state within themselves. Here's a basic example:
```
import React, { useState } from 'react';

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

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}
```


**useEffect():**
`useEffect()` enables developers to perform side effects in functional components. It replaces componentDidMount, componentDidUpdate, componentWillUnmount lifecycle method. Here's the basic structure of useEffect:

```
 useEffect(() => {
   // Side effect code
   // This code will run after every render
   return () => {
     // Cleanup code (optional)
     // This code will run before the component unmounts
   };
 }, [/* dependencies */]);

```
useEffect contains two arguments `first` is a function, in This function is the side effect you want to perform. It will run after every render by default. You can return a cleanup function from this function (optional) to perform cleanup before the component unmounts and the `second` is an array of dependencies. It's an optional array that contains values (usually props or state) that the effect depends on. If any of the dependencies change between renders, the effect will be re-run. If you omit this array, the effect runs after every render. If you provide an empty array, the effect only runs once.

Here's an example to illustrate useEffect:

```
import React, { useState, useEffect } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(prevSeconds => prevSeconds + 1);
    }, 1000);

    return () => {
      clearInterval(interval);
    };
  }, []); // Empty dependencies array means the effect runs only once

  return (
    <div>
      Seconds: {seconds}
    </div>
  );
}

export default Timer;
```


**useRef():**
`useRef` is a hook in React that provides a way to create mutable references that persist across renders without causing the component to re-render when the reference changes. It's commonly used to access DOM elements or to persist values between renders without triggering a re-render.

```
import React, { useRef, useEffect } from 'react';

function InputWithFocusButton() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current.focus();
  }, []);

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={() => inputRef.current.focus()}>Focus the input</button>
    </div>
  );
}

export default InputWithFocusButton;
```
In this example:
We have InputWithFocusButton component that contains an input element and a button. We use `useRef` to create a reference to the input element(inputRef). We use useEffect with an empty dependency array to focus the input element when the component is mounted. We set the `ref` attribute of the input element to `inputRef`, allowing us to access the input element using inputRef.current. We use `inputRef.current.focus()` to focus the input element both in the useEffect and when the button is clicked.


**UseMemo()**
`useMemo` is a hook that is used for memorization in React. Memoization is a technique to optimize performance by caching the result of expensive function calls and returning that cached result when the same inputs occur again.
In `useMemo()`we pass a function as the `first` argument. This function is an expensive computation you want to memorise. The `second` argument is an array of dependencies.  The memoized value will only be recalculated if one of these dependencies changes.

Example:

```
import React, { useMemo, useState } from 'react';

function Fibonacci({ n }) {
  const calculateFibonacci = (n) => {
    if (n <= 1) return n;
    return calculateFibonacci(n - 1) + calculateFibonacci(n - 2);
  };

  const result = useMemo(() => calculateFibonacci(n), [n]);

  return <div>The {n}th Fibonacci number is {result}</div>;
}

```
In this example, calculateFibonacci is an expensive function to calculate Fibonacci numbers. We use `useMemo` to memoize the result of `calculateFibonacci(n)` based on the n prop. This ensures that the Fibonacci calculation is only performed when the n prop changes.

**useCallback()**
`useCallback` is a hook that is used to memoize functions, similar to `useMemo`, but specifically for functions. It returns a memoized version of the callback function that only changes if one of the dependencies has changed.

Example:

```
import React, { useCallback, useState } from 'react';

function CounterButton({ onClick }) {
  return <button onClick={onClick}>Increment</button>;
}

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

  const increment = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <CounterButton onClick={increment} />
      <p>Count: {count}</p>
    </div>
  );
}

```

In this example, an increment is a callback function to increment the count state. We use useCallback to memoize this function based on the count state. This ensures that the increment function reference remains stable across renders unless the count state changes.

**useContext**
`useContext` is a hook in React that allows functional components to consume context that has been provided by a parent component.

Structure:

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

const MyContext = React.createContext(defaultValue);

function MyComponent() {
  const value = useContext(MyContext);

  // Use value in your component

  return (
    // JSX for component
  );
}

```

- First, you need to create a context using `React.createContext()`. You can optionally pass a default value to the context. This default value will be used when a component does not have a matching provider.
- Define your functional component where you want to use useContext.
- Call useContext() and pass the context you created as an argument. This hook returns the current context value for the given context.

Example:

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

const ThemeContext = React.createContext('light');

function ThemedButton() {
  const theme = useContext(ThemeContext);

  return <button style={{ background: theme }}>Themed Button</button>;
}

function App() {
  return (
    <div>
      <ThemeContext.Provider value="dark">
        <ThemedButton />
      </ThemeContext.Provider>
    </div>
  );
}
```
In this example, ThemedButton component consumes the ThemeContext using useContext. It changes its style based on the current theme provided by the context.


**useReducer**
`useReducer` is a hook in React that is used for state management, similar to `useState`, but it's more suitable for managing complex state logic.

Example:

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

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

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

- Define a `reducer` function, it takes the current state and an action as arguments and returns the new state based on the action. It's similar to the reducer function in Redux.
- Define your functional component(`Counter`) where you want to use useReducer.
- Call useReducer() and pass the reducer function and initial state as arguments. This hook returns an array with the current state and a dispatch function.

The counter component uses `useReducer` to manage the state of a count. It dispatches actions (INCREMENT and DECREMENT) to update the count based on user interactions with buttons.

These are some of the most commonly used hooks in React applications, but there are many others available. Each hook serves a specific purpose and can help improve the readability, performance, and maintainability of your code.


Discover the art of crafting custom hooks in ReactJS. [Click here](https://dev.to/jaingurdeep/supercharging-react-components-with-custom-hooks-53ci) to dive in!