You have to know closures to be a (good) React developer

I hear a lot about the misconception that being a React developer does not mean you have to have a deep understanding of how JavaScript works. After all, it makes sense — the average React developer, most of the time, does not use more than ternary expressions and some array functions (map, filter, etc.), right?

Well, it turns out, that if you want to be a more than average React developer, you need a solid grasp of how JavaScript actually works behind the scenes — closures, prototypes, and reference/primitive value types, are just some of the concepts you just have to master.

In this article, I will show you, how understanding closures improve your React skills. This article is NOT about explaining how closures work. I will show a brief example, but if you want to understand closures I suggest searching for another resource (like this). Excited? Let’s go!

What is a closure?

Let’s see what MDN has to say about closures:

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

OK, that’s the official definition. Sounds a little intimidating? Lets simply it: Closure means, that when JavaScript runs your code, it looks for all variables in your functions, and if it sees a variable in a function that has no declaration (- let, const) inside of it, but in an outer scope (where the function is nested in) it “locks” the value for that variable inside that given function.

Are you still confused? Consider the following snippet:

Notice how we use “variableInOuterFunction” (which is declared in myOuterFunction) inside “myNestedFunction”. That’s a closure. The value “Hello World” is now “closed” (locked) in the nested function. Sounds pretty simple, right? Let’s see how understanding it helps us in React.

Closures in React

So, let’s see how closures work in React.

Consider the following snippet:

So what do we have here?

It’s a simple React component that prints a timer on the screen which increments itself every second. We use the useState hook to keep track of changes in the count variable, then we have the incrementCount function which increments the count variable by using the setCount function, and finally, we have the useEffect hook that is in charge of triggering the incrementCount function every second using the built-in JavaScript function setTimeout.

Experienced React developers probably already noticed that we have in the dependency array of the useEffect hook the function incrementCount. Actually, it might not have been necessary to include it here, but it is considered a good practice to always fill the dependency array with all variables (and functions) that are used in the useEffect hook.

Now we have another issue. With the incrementCount function as a dependency in useEffect hook, my IDE laterally shouts at me:

Add alt text

We will not dive into why this is happening right now, I’ll just say it has to do with the fact that functions are reference types, and if we don’t move it inside the useEffect hook or wrap it with the useCallback hook, we can cause an infinite loop. Anyhow, for us what’s important right now is that that suggestion from the IDE — either move incrementCount inside useEffect or wrap it with the useCallback hook. Let’s choose the second option.

Great! Now everything should work, right? I mean even my IDE is now calm and quiet again let’s run the code!

We see Timer started:1, 2 and…What happened? The timer just stopped on 2? Now, why is that happening?

Add alt text

The reason for that — you guessed right — closures! If we look carefully we’ll see that inside the incrementCount function we actually have a closure. It is a bit difficult to notice but when we use the setCount function we rely also on the count variable we could also write it like this:

The reason for writing the longer version is that when we rely on the previous state we should take the most current state and it is a good practice to do so (although not necessarily required in our example).

Anyway, back to closures — as we can see in our example the value for the variable count which is declared in the outer function ClosuresInReact is now “locked” inside the function incrementCount. To be even more precise, the incrementation that we do in the setCount function is locked inside the incrementCount function, which is why we see on the screen 1, 2, and… stop.

How to fix the closure issue in React

So let’s simplify the process that happens here:

React: I have a useEffect hook that has incrementCount as a dependency. It means I should only re-render when incrementCount changes.

React: I have incrementCount wrapped with the useCallback hook. It means I should memorize the first closure of it (when it ran first) and even if a re-render happens I should not run the function with the refreshed environment (when count is now 2) but with its good old first closure I captured (when count is 1).

React: useEffect tells me that incrementCount is not changing, so I should not trigger a new render.

You get the point. So what should we do? The solution is simple. We need to pass the count variable as a dependency to the useCallback hook. By that, we are telling React that even though we want to keep the first closure of the incrementCount function, React should keep track and update the closure with the most recent value of the count variable. By doing this, we are also causing the useEffect hook to invoke a re-render on the screen because the incrementCount changes each second.

Now everything is working as expected. Funny thing to see that my IDE now still yells at me:

Add alt text

It tells me to remove the count from the dependency array. This is of course a mistake, and it happens because we don’t actually use the count variable in the setCount function, but it only shows us again how important it is to understand closures to be a good React developer because without that we could not solve the problem.

Conclusion

Closures in JavaScript is a crucial concept and when it comes to React it may become even more important to grasp what’s going on behind the scenes. React itself, and especially React hooks, takes the usage of closures in plenty of places.

Mastering the concept of closures, with the rest of the core concepts of JavaScript (prototypes, reference and primitive types, and so on) makes you a better React developer and helps you solve problems in a more efficient and elegant way.

A Freelance MERN fullstack developer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store