Interesting points from Abramov’s “A Complete Guide to useEffect”

Mark Romano
Level Up Coding
Published in
4 min readMay 29, 2019

--

limes are good :3 (Hoach Le Dinh)

I’ve been using hooks quite a bit this year, along with redux hooks, in personal and production projects. The API is a little rough around the edges — but they generally live up to the hype. I’ve been able to refactor apps relatively seamlessly, and making all components functional is really very kewl.

Dan Abramov wrote very freakin’ long and helpful blog post on the topic of useEffect a couple of months ago (I’m late to the party ik ik). My focus here isn’t the TL;DR (he has his own summary), or even my own takeaways from the article, but some interesting behaviors of the hooks API that I didn’t pick up in the docs or the tutorials I had read. They were kind of a side note in Dan’s piece, but given how foreign these ideas were to me, I think they’re worthy of at least a small post such as this.

cleanupCallback != componentDidUnmount

The first is around the optional cleanup function you can return from the callback passed into useEffect. From the official react docs:

When exactly does React clean up an effect? React performs the cleanup when the component unmounts. However, as we learned earlier, effects run for every render and not just once. This is why React also cleans up effects from the previous render before running the effects next time.

I think as soon I read that it’s run when the component unmounts, my brain ignored the rest of that statement. I’ve been so locked into the lifecycle mindset, and that classes == hooks, I didn’t acknowledge useEffect's novel feature of running it’s cleanup anytime the useEffect callback is run again. Anytime that the useEffect callback is run, before it can be run again, the cleanup callback will be run. It has some interesting implications, though to be honest at the moment no practical applications of this behavior spring to mind 😅.

An effect callback will run after every render unless we specify an array of deps. If we specify deps, it will run only when that dep changes between renders. And if that happens, the cleanup callback will have to run too. It doesn’t only happen when the component unmounts. But in fact, we should probably not really think of our component as “unmounting” with a hooks component, but rather the end of just another, albeit final, render.

You can see this irl by playing with this example. Check the console and you’ll see with every press of the button, the log inside of the callback is printed, as well as the log inside of the cleanup function.

dispatch is a cool hax

Dan created an example of an effect that makes use of two state variables. One of the variables calculated value is dependent on the other, so that other variables must be listed as a dep. You can see this in detail in his example here, or just peep the snippet below

In the above example, we’re using a callback to change the values of count , which allows us to avoid passing it into our array deps. But since it’s calculated value is dependent on step , we must pass step into the array. The consequence of that when step is changed, the effect callback AND the cleanup functions are both called. If for some reason you don’t want the effect and it’s cleanup function being called anytime you can summon dispatch .

dispatch in an effect is sort of like remotely accessing and modifying your state. dispatch sends an action from inside the effect to its corresponding reducer , decoupling the effect from the state, and allowing you finer control over when your effect is run. An example can be found here.

This actually was a major point Abramov was trying to convey, but I included it here because this behavior of the dispatch isn’t obvious from the official docs. I also wanted to include it because it shows the utility of useReducer . In first checking out the docs around this method, I was a little underwhelmed. I believed it was just a redux style global reducer moved to a component state. Now I can see it’s a bit more capable.

While these were the points that got me shook, there are a lot of other very helpful points in Dan’s article, and even though it’s long af, I encourage all to read it or atleast the TL;DR. Much of the article encourages adapting a new mental model for rendering phases, and even though I can’t say I get it 100%, I will say it helped me quite a bit.

Another, more spicy takeaway — the hooks API might need a smidge more polish and abstraction before going prime time, especially around useEffect . Memoizing functions, hoisting static functions outside the component body, creating lengthy reducers, raises some barriers to entry imo. Or maybe I’m just being close minded and not seeing things right. Let’s chat in the comments!

🐈

--

--