React useEffect Hook

Summary: in this tutorial, you will explore the React useEffect() hook, and learn how and when to use it properly.

Understanding React useEffect hook

In React, side effects include data fetching, manual DOM manipulation, or any other operations that impact outside of the component.

To perform side effects in function components, you use the useEffect hook. To use the useEffect hook, you first import it from the react library:

import { useEffect } from 'react';Code language: JavaScript (javascript)

Then, you can use it in your component with the following syntax:

useEffect(setup, dependencies)Code language: JavaScript (javascript)

The useEffect hook is a function that takes two arguments:

  • setup is the main function containing side effects, which you want to execute.
  • dependencies is an optional array of dependent values that determine when the setup function should run. The useEffect hook will run the setup function based on the items specified in the dependencies array.

The setup function can optionally return a cleanup function:

return cleanup;Code language: JavaScript (javascript)

React executes the cleanup function before it unmounts the component or before the setup re-runs (except the first time).

In the cleanup function, you may include code for cleaning up resources such as clearing intervals or canceling a network request (AbortController).

Typically, you’ll write the useEffect hook as follows:

useEffect(() => {
  // Side effect code here

  return () => {
    // Cleanup code here (optional)
  };
}, [dependencies]);Code language: JavaScript (javascript)

In this syntax, you define an arrow function for the setup function:

() => {
  // Side effect code here

  return () => {
    // Cleanup code here (optional)
  };
}Code language: JavaScript (javascript)

And include values in the dependencies array:

[dependencies]Code language: JavaScript (javascript)

Run an effect only once

To run an effect ( or a function) once when the component mounts, you use an empty array as the dependencies array:

useEffect(() => {
   // This code runs once
}, [])Code language: JavaScript (javascript)

In practice, you use this pattern to run a function during the initial render of the component. For example, you can run a function that fetches data from an API only once when the component mounts.

The following UserList component displays a list of users by calling the API endpoint https://jsonplaceholder.typicode.com/users:

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

export default function UserList() {
  const [users, setUsers] = useState([]);

  const fetchUsers = async () => {
    const url = 'https://jsonplaceholder.typicode.com/users';
    const response = await fetch(url);
    const data = await response.json();
    setUsers(data);
  };

  useEffect(() => {
    fetchUsers();
  }, []);

  return (
    <div>
      <h1>User List</h1>
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}Code language: JavaScript (javascript)

Run an effect whenever dependencies change

To run a function when certain values change, you can specify those values in the dependencies array.

The following CounterTitle component utilizes the useEffect() hook to update the document title when the count state variable changes:

import { useState, useEffect } from 'react';

const CounterTitle = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  return (
    <div>
      <h1>Current count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default CounterTitle;Code language: JavaScript (javascript)

Run an effect after every rerender

To run a function during the initial render and after every rerender, you pass only the setup function to the useEffect() function and skip the dependencies array.

For example, you can log a message when the component renders and every time it re-renders by using the useEffect() function:

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

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

  useEffect(() => {
    console.log('Component rendered or rerendered');
  });

  return (
    <div>
      <h1>Current count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default Counter;Code language: JavaScript (javascript)

Run a cleanup function

The following WindowDimension component listens for window resize events and updates the width and height state variables based on the window’s size:

import { useState, useEffect } from 'react';

function WindowDimension() {
  const [width, setWidth] = useState(window.innerWidth);
  const [height, setHeight] = useState(window.innerHeight);

  useEffect(() => {
    // Define the handler function that updates the state with the window's width
    const handleResize = () => {
      setWidth(window.innerWidth);
      setHeight(window.innerHeight);
    };

    // Add the event listener for window resize
    window.addEventListener('resize', handleResize);

    // Cleanup function: Remove the event listener when the component unmounts
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  return (
    <div>
      <p>
        Current dimension: {width}x{height}px
      </p>
    </div>
  );
}

export default WindowDimension;Code language: JavaScript (javascript)

How it works.

First, define handleResize() function to update the width and height state varibles based on the window’s innerWidth and innerHeight:

const handleResize = () => {
   setWidth(window.innerWidth);
   setHeight(window.innerHeight);
};Code language: JavaScript (javascript)

Second, register an event listener for the resize event using the handleResize() function:

 window.addEventListener('resize', handleResize);Code language: JavaScript (javascript)

Third, return a cleanup function from the useEffect, which removes the resize event listener uisng window.removeEventListener method:

return () => {
      window.removeEventListener('resize', handleResize);
};Code language: JavaScript (javascript)

This prevents the component to call the handleResize function after it is removed from the DOM, which may lead to memory leak and unwanted behavior.

Summary

  • Use useEffect(setup) to run the setup function when the component renders and re-renders.
  • Use useEffect(setup,[]) to run the setup function once during the initial render.
  • Use useEffect(setup, [dependencies]) to run the setup function when the component renders and every time the items in the dependencies array change.
Was this tutorial helpful ?