A Look At React Hooks: useEffect

A Look At React Hooks: useEffect

Welcome to A Look at React Hooks, a beginner-friendly series on React Hooks. In this article, let's look at the useEffect Hook.

What is useEffect?

If you are coming from the class-based React, the useEffect Hook replaces the componentDidMount, componentDidUpdate, and componentWillUnmount lifecycle methods.

In simple terms, useEffect helps us to deal with side effects like data fetching and state or variable changes for React functional components. It takes 2 arguments:

  1. A function that will instruct what to run when a state changes
  2. A dependency array that can contain a list of variables that tells the Hook to run every time its value is updated. If not supplied, the Hook will run after every render.

Let's see how it works.

Initialization

The very first step is to import the Hook:

import React, { useEffect } from 'react';

To use the Hook, simply add it to the top level of the function component:

useEffect(() => {
    console.log('This runs after every render');
});

In this example, the Hook will run after every render.

Runs Once on First Render

If you want the Hook to run only once on first render, we can add an empty array as its dependency like so:

useEffect(() => {
    console.log('This runs once on first render');
}, []);

By supplying an empty array as the second argument, this tells the Hook to listen for zero state changes. Hence, it will only run once.

Runs On a Specified Dependency

To make the Hook run on a particular state change, we can supply the variable as a dependency in the array.

 useEffect(() => {
    console.log("Count variable has changed!")
 }, [count]);

In this example, the Hook will run on first render and after every time the variable count has changed its value.

What about for multiple variable changes?

If we want the Hook to run every time after multiple variable changes, simply add them to the array like so:

 useEffect(() => {
    console.log("Some count variable has changed!")
 }, [count, count1, count2]);

useEffect with Cleanup

For the Hook to run right before the component unmounts, just like componentWillUnmount, simply use its return function like so:

useEffect(() => {
    console.log('This hook is running.');

    return () => {
        console.log('This hook is now unmounting.');
    };
});

Important Things to Note

Now that we have learned the basics of this Hook, let's look at some important things to note about useEffect.

1. Dependencies

The dependencies supplied in the array as the second argument of useEffect must be variables from the component scope such as props or state.

Changing Dependencies Too Often

While dependencies allows optimization for the Hook, it can cause issues when the values change too often, as mentioned in the React Documentation. Take a look at the example below.

Our Counter component uses the useState hook to declare a variable named count. And a useEffect hook is used to increment the count state by interval. However, by supplying count as a dependency, the interval will be reset every time the hook is re-run.

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

  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1); // effect updates and depends on count
    }, 1000);
    return () => clearInterval(id);
  }, [count]);  // Hence count must be specified as a dependency

  return <h1>{count}</h1>;
}

Source: reactjs.org/docs/hooks-faq.html#what-can-i-..

To fix this, we should supply an empty array dependency and updatecount without setCount referencing its current state as shown below.

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

  useEffect(() => {
    const id = setInterval(() => {
      setCount(c => c + 1); // Doesn't depend on count outside
    }, 1000);
    return () => clearInterval(id);
  }, []); // useEffect doesn't use any variable dependencies

  return <h1>{count}</h1>;
}

2. Runs After Render

useEffect runs after a prop, state or context changes. It runs after every render, unless specified by the dependency array. Instead of thinking in terms of lifecycle methods like in class-based React, it is better to think about what effect should happen on the component after the re-render.

3. useEffect Returns

It is important to note that the useEffect hook can only return a function for cleanup or null. If you try to use an async function inside the hook, it may return an error because the hook returns a Promise and not null.

Conclusion

And that's the gist of this hook! Thanks for reading this article. I hope it was helpful for React beginners. Please feel free to ask questions in the comments below. Ultimately, practicing and building projects with this hook will help anyone to pick it up faster.

The next hook in this series will be: useContext(). Stay tuned and cheers!

Did you find this article valuable?

Support Victoria Lo by becoming a sponsor. Any amount is appreciated!

ย