How does useEffect really work?

Charlie Kroon
3 min readApr 12, 2023

--

In the month of April, I’ll publish a small blog series called “Questions from the Past”. I’ll be going through my old notebooks from my first year as a developer and answering the questions I think are most important. Today we will discuss the workings of the useEffect hook.

useEffect is a React Hook that can be both useful and messy at the same time. I know there are already a bunch of blog posts on this, but I want to make it super clear for you when, how, and why you can use it. Let's go into it!

You can use useEffect to perform various tasks, such as fetching data, updating the DOM, optimizing performance, or cleaning up resources when the component is unmounted*.

Let’s take a look at the useEffect hook, and analyze bit by bit what it does. To begin, here's the basic code for the useEffect hook:

useEffect(() => {
// code to run on every render
return () => {
// clean up!
}
}, [dependencyArray]);

The dependency array

The dependency array is the second parameter of useEffect. It’s literally called the ‘dependency array’ because useEffectis dependent on this array. With that, I mean that depending on what's inside the dependency array, your useEffect will either run or not run.

There are three things that you can do with the dependency array:

  • Leave it empty
    useEffect will run only once when the component loads for the first time. This is because there are no dependencies that could trigger a re-run of the code.
useEffect(() => {
console.log("The component was loaded for the first time!")
}, []);
  • Don’t include it at all
    useEffect is called on every single state change.
useEffect(() => {
console.log("I ran because there was a state change!")
});
  • Put a variable inside the dependency array
    useEffect will run every time that variable changes.
const [dogCount, setDogCount] = useState(0);

useEffect(() => {
console.log(`There are now ${dogCount} dogs!`)
}, [dogCount]);

The return function

The return function (aka the Clean Up function) is called when the component is about the be removed from the screen, or when the useEffect needs to cancel the previous effect.

For example, in this code snippet, the useEffect will fetch user data every time the value of userId changes.

const [user, setUser] = useState(null);

useEffect(() => {
let isRendered = true;

fetchUserData(userId).then(user => {
if(isRendered) setUser(user);
});

return () => {
isRendered = false;
};
}, [userId]);

When the component is first mounted, it uses the fetchUserData function to get the data from the initial userId. If the userId changes while the component is still showing, the useEffect hook makes sure that component only displays the data for the current userId and not the old data.

When the fetchUserData function returns with the user data, it uses setUser to update the component with the new data. This makes sure that the component always shows the correct user data.

So, to sum it up: if you’re using React, it’s important to know how useEffect works. I know it can be confusing at first, but once you get the hang of it, it can really help make your React app better and faster.

*Component mounting is when React creates and adds a new component to the page for the first time. It sets up the component’s initial state, properties, and children, and assigns it a unique identifier. After it’s mounted, it can be updated with new information.

--

--