React Tricks: Customizing your useEffect to run ONLY when you want!

How the useEffect Hook Works (with Examples)

INTRODUCTION

The useEffect hook in the world of React’s functional components operates as a componentDidMount, componentDidUpdate and componentWillUnmount – all in one. But what if you only wanted to trigger one of the effects at a time. How would you accomplish this? Before we dive into it, let’s understand a little more about how the useEffect works.

Useeffect – render cycle & dependencies

First let’s understand how the useEffect works in the render cycle. Look at the component below, and think about 3 questions – In what sequence will the console logs be printed:

  1. When the component mounts?
  2. When the count is incremented?
  3. When the component unmounts
SOLUTION
When the component mounts:
  1. Starting of component 0
  2. Before return 0
  3. Mounting the component 0
  4. Count set to 0
When the count is incremented
  1. Starting of component 1
  2. Before return 1
  3. Count cleanup 0
  4. Count set to 1
When the component unmounts
  1. Unmounting the component 0
  2. Count cleanup 3

Did you get it right? I didn’t!

Here’s what we learn from this experiment:

  1. None of the useEffects run till after the component has executed the return statement. That’s why “Starting of component” and “Before return” were logged before “Mounting the component” and “Count set to 0”.
  2. Once the component has rendered i.e. returned the JSX, it runs the useEffects in the order that they were declared in (this can be very useful to know, as we will see)
  3. When the count is incremented, the console logs at the beginning and end run before the useEffect (and they run with the updated value of count!). That means, even when the component state changes, useEffects run only after the component has executed the return statement.
  4. After the count is incremented and the return statement executed, the cleanup function returned in the useEffect for count runs – but it runs with the old value i.e. 0. Finally, after the cleanup, the main body of the useEffect runs with the new value of count i.e. 1.
  5. When unmounting, ONLY the functions returned from the useEffects run with the value of count as per what it was when the useEffect ran lasts. For the unmount cleanup effect with no dependencies, the count printed 0. For the count cleanup effect, the count printed 3 (which was the last value of count)

This exercise should have clarified a lot about how useEffects work. Notably, the cleanup function will run everytime the state of one of the dependencies changes and not only when the component unmounts.

Here’s another situation – all we have done is declared a stable object to the component and mentioned it in the dependency array. What do you think will happen now as the component state changes?

SOLUTION
WHEN THE COMPONENT MOUNTS
  • Mounting the component 0
WHEN THE COUNT IS incremented:
  • Unmounting the component 0
  • Mounting the component 1

Wait! Whaaaat?

Why is the effect running when the count changes, even though it’s not in the dependency array? Because, everytime the state changes, the variable stableObject is declared again. When it is declared again, it’s address/reference changes. When the component checks whether or not to run the effect again, it compares the old instance of stableObject and the new one. Although the values are the same, since the references are different, React feels that the effect needs to run again. This is something you need to be mindful of while writing your useEffects. As far as possible, ensure that you have primitives in the dependency array, and if you don’t, ensure that your variables have a stable signature by wrapping them in a useMemo.

Now that we have a grasp on how the useEffect works, lets move on to configuring it as per our requirements.

RUNNING AN EFFECT ONLY ON MOUNT

If you want to run an effect only on mount, and the effect depends on a stateful variable, all you need to do is declare the useEffect without that variable in the dependency array. This is completely okay! The effect will run with the initial value of the state variable and won’t run again.

The only thing to be aware of is that if you have a cleanup function returned from the effect, it will also run with the same initial value of count – and not the updated one.

SKIPPING AN EFFECT ON THE FIRST RENDER

Many a times, we come across situations where we don’t want to run an effect when the component mounts, but only when its dependency changes thereafter. Here’s how you can do that.

We could accomplish the same thing using the useState hook instead of useRef variable. But that would cause a re-render immediately when the component mounts. When a ref is changed, it doesn’t cause a re-render.

Running a cleanup only once on unmount

Here we use a very similar technique to control the unmount. Important to note here, the order in which you declare the useEffects is critical. If you swap the order of the useEffects, the cleanup effect will not work. This is because the useEffects and cleanups run in the order that they are declared.

MANAGE IT ALL WITH A CUSTOM HOOK

Here’s a custom hook you can use to manage when your useEffects run!

Summary: 

In this article, we have drawn a picture of how a customized ‘React’-based solution can help you manage running your useEffects, without any hassle in between. Moreover, in the above picture, we have provided a reference for a custom hook that you can rely on while your useEffect runs.

3 thoughts on “React Tricks: Customizing your useEffect to run ONLY when you want!

  1. Hey, very useful article! Could you explain more how to correctly use useMemo hook in example with stableObject or with arrays of that objects?

Leave a reply to Rahul Shah Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.