DEV Community

Cover image for Mastering React Hooks: A Comprehensive Guide with Examples
Gurdeep Jain
Gurdeep Jain

Posted on • Edited on

Mastering React Hooks: A Comprehensive Guide with Examples

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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 */]);

Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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>;
}

Enter fullscreen mode Exit fullscreen mode

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>
  );
}

Enter fullscreen mode Exit fullscreen mode

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
  );
}

Enter fullscreen mode Exit fullscreen mode
  • 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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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 to dive in!

Top comments (0)