How to use React's useEffect() hook

In this article, I want to explain everything you need to know about the useEffect React hook.

What is the useEffect hook about?

The useEffect is a powerful React hook that can be used for side effects in functional components.

We can use the useEffect hook to react to or to perform side effects.

What do I mean by side effects? Side effects are events that are out of the scope of the component. For example, we could use the hook to make an API call when a prop updated.

The functionality of this hook replaces most of the lifecycle functions we know from functional components.

How to use the useEffect hook

The react hook accepts two arguments: A callback and optionally an array of values.

React.useEffect(() => {
  console.log('Hi from the effect hook!');
}, [someVariable]);

Without a second argument, the callback function will be executed every time the component renders or re-renders.

The second argument accepts an array of values. If any of these values change after an update, the callback function will be executed.

The cleanup function

We can return a function from the callback, which will be executed before the component unmounts or before the callback gets called again:

React.useEffect(() => {
  console.log('Hi from the effect hook!');
  return () => {    console.log('Clean up');  };}, [props.userId]);

This function is usually used to clean up the component by canceling network requests or invalidating timers.

The output for the code above will be the following when props.userId changes:

Hi from the effect hook!
Clean up
Hi from the effect hook!

The cleanup function will only be called before userId changes in order to clean up any dependencies from the last execution.

If the second argument is empty, it will be called when the component unmounts.

How to replace React's lifecycle functions with useEffect

The useEffect hook doesn't aim to port the exact same behavior of all lifecycle functions to functional components.

The possible replacements I'll show you here might differ slightly in timing. However, the hook offers the functionality that will allow you to replace your lifecycle functions in most cases. In this section, I want to explain how you can do this.

How to access componentDidMount in function components

To mimic the behavior of componentDidMount in functional components, we need to pass an empty array as the second argument to the useEffect hook.

React.useEffect(() => {
  console.log('Hi from the effect hook!');
}, []);

Passing an empty array makes the hook execute the callback only once when the component is mounted since none of the values of the array will ever change.

How to access componentDidUnmount in function components

In functional components, we use the cleanup function to mimic componentDidUnmount.

For this, we need to add an empty array as the second argument and return a function in the callback. The code inside the function that we return will be executed when the component unmounts.

React.useEffect(() => {
  console.log('Hi from the effect hook!');
  return () => {
    console.log('Component unmounts');
  };
}, []);

We shouldn't update the state in this function as the component won't be re-rendered.

How to access componentDidUpdate in function components

In the first example we saw that, without a second argument, the callback gets executed on every update.

If you've been working with componentDidUpdate in the past, you're probably used to comparing the current props with the previous ones:

componentDidUpdate(prevProps) {
  if (prevProps.userId !== this.props.userId) {
    this.fetchUser();
  }
}

The useEffect hook makes this easier by allowing us to choose to which properties or state changes we want to react.

In a functional component, the above code would look like this, which, in my opinion, is much easier to read and write:

React.useEffect(() => {
  fetchUser();
}, [userId]);

Advanced example: Why doesn't useEffect work?

I only really understand something if I've tried it out myself. That's why I've created a small codepen to play around with this hook.

I wanted to find out was whether the hook would update when I add a value that's not a prop or state to the second argument.

I created two buttons: One that increments the counter in the window's title, the other one increments a counter that has been initialized with React.setState(1).

Then I added document.head.title to the array of values in the second argument of useEffect. The callback prints out the current values.

const App = () => {
  const [state, setState] = React.useState(1);
  React.useEffect(() => {
    console.log('The title changed!')
    console.log('State changes: ', state, 'Title changes: ', document.head.title);
  }, [document.head.title]);
  const incrementTitle = () => {
    const currentCount = parseInt(document.head.title, 10);
    document.head.title = isNaN(currentCount) ? 1 : currentCount + 1;
  }

  return (
    <>
      <button onClick={() => incrementTitle()}>Increment title count</button>      <button onClick={() => setState(state + 1)}>Increment state change count</button>    </>
  );
};

See the complete example here.

If you play around with this a bit on codepen, you will see that it only updates when the following two conditions are met:

  • A state update occurred.
  • The values of the array in useEffect have changed.

Thinking about this a bit, it makes a lot of sense: React doesn't listen to changes of the DOM as it only re-renders when React's state is updated.

You certainly can pass any values to the array of useEffect, but don't expect the function to be called right away if these values aren't part of React's state.

Conclusion

I hope that this article gave you a better understanding of how to use React's useEffect hook. If you have any open questions or feedback for me, feel free to leave a comment!

If you liked this article, you might also want to check out my other articles about React:

  • How to use React's useRef() hook

    At first sight, it may not be clear what purpose the useRef hook fulfills. If you know how to use it, however, it can be a useful tool in certain situations.

    In this article, I'll explain everything you need to know about it, including how, when, and when not to use it.

  • When does React re-render components?

    This article does a deep dive into how React's rendering mechanism works. If you want to learn more about the VDOM and how to optimize render performance, I recommend that you check it out.

  • Unit testing React - What you need to know

    This article gives you a high-level overview of how to get started with unit testing in React by covering the basics of unit testing and looking at the different tools that you can choose.

PatreonKoFi