August 26, 2020
0 strokes bestowed

Mental Model of Use Effect

edit

One thing I have found challenging about React.useEffect is the mental model regarding the dependencies array.

It's very easy to get it in your head to use the dependency array as a means to constrain the calling of the effect, essentially treating it as a guard or conditional.

"If this value changes, do that effect."

The problem is that you'll often run into issues where you only want to run an effect when a single value changes, and thus you'll be tempted to leave out the other dependencies from the dependency array. Next thing you know, you'll be facing an ESLint warning about it. Like with the following code:

React.useEffect(() => {
  // Pretend these values are in the parent scope
  const diff = prevQuantity - quantity
  someHandlerFunction(diff)
}, [quantity])

This, more or less, works like we want it to. When quantity changes, we call the effect and call the handling function. However, this will also warn you that you haven't added all the dependencies. You might think, "I don't want this effect to run when the handler function changes! That'll break the code!"

Trust me, I get stuck in this loop a lot, too.

Here's what you need to understand. Simply put, not calling the effect whenever a dependency changes means you'll have stale values and functions. It will lead to bugs.

Instead, you must make the mental shift to:

Call this effect whenever the dependencies change, but add a conditional in the effect function that only triggers the changes you need, when you need them to happen.

The more correct code looks like this:

React.useEffect(() => {
  if (prevQuantity !== quantity) {
    const diff = prevQuantity - quantity
    someHandlerFunction(diff)
  }
}, [prevQuantity, quantity, someHandlerFunction])

Now we can call the effect whenever a dependency changes, while still not calling our handler function when we don't intend to.

Fixing this small mental model issue helps you avoid the ESLint warnings and write better code. Hope this helps you.


Update - August 27, 2020

I had an additional thought regarding this topic and wanted to add it to the post. Another way to think about the useEffect hook is to write the code as if the hook and the dependency array wasn't there first, and then wrap it in the hook and add the dependencies later.

If I think about this outside of the hook, my mental model for the functionality is, "If there's a difference between these two values, send the difference to this handling function." When we write that code, it looks like the body of our effect, but it makes sense in isolation.

if (prevQuantity !== quantity) {
  const diff = prevQuantity - quantity
  someHandlerFunction(diff)
}

Now that we've written the functionality we desire, the next step is to put React in charge of tracking all of these values for us.

React.useEffect(() => {  if (prevQuantity !== quantity) {
    const diff = prevQuantity - quantity
    someHandlerFunction(diff)
  }
}, [prevQuantity, quantity, someHandlerFunction])

Hope this little update also helps you in your mental understanding of the useEffect hook.

Sharing this article on Twitter is a great way to help me out and I really appreciate the support.
+0
Liked the post? Click the beard a few times.
Tags
JavaScriptReact
Newer Post: Multiple Boolean Props are a Code Smell

Let's talk some more about JavaScript, React, and software engineering.

I write a newsletter to share my thoughts and the projects I'm working on. I would love for you to join the conversation. You can unsubscribe at any time.

Data Structures and Algorithms Logo
Data Structures and Algorithms

Check out my courses!

Liked the post? You might like my video courses, too. Click the button to view this course or go to Courses for more information.
View on egghead.io
Kyle Shevlin's face, which is mostly a beard with eyes
Kyle Shevlin is a front end web developer and software engineer who specializes in JavaScript and React.