Skip to content
Today's Tech Byte: Closures in React

Today's Tech Byte: Closures in React

Posted on:November 20, 2023

Scope and closures are two of the most important concepts in JavaScript. They are also the most misunderstood. In this chapter, we will learn about closures and how they are used in React.

Scope and Closures

In JavaScript, scope is the set of variables, objects, and functions you have access to at a given time. There are two types of scope: global scope and local scope.

Every time you create a function, a new scope is created. This is called function scope. In JavaScript, variables are scoped to the function in which they are declared. This means that variables declared inside a function are not accessible from outside the function.

Closure is a function that has access to its parent scope, even after the parent function has returned. In other words, a closure is a function that remembers the environment in which it was created. This means that a closure can access variables from its parent scope, even after the parent function has returned.

Stale Closures

Let’s take a look at this example:

const something = value => {
  const inside = () => {
    console.log(value);
  };
  return inside;
};

something("Hello")(); // Hello
something("World")(); // World

This works as expected. But what if we cached the inside function and called it later?

const cache = {};
const something = value => {
  if (!cache.current) {
    cache.current = () => {
      console.log(value);
    };
  }
  return cache.current;
};

something("Hello")(); // Hello
something("World")(); // Hello

Hmmm 🤔. This is not what we expected. The inside function is supposed to remember the environment in which it was created. But it doesn’t. It remembers the environment in which it was called. This is called a stale closure.

A function takes a snapshot of its parent scope when it is created, and because we are caching the function, it is not being recreated. So, it is not taking a new snapshot of its parent scope. It is using the same snapshot every time it is called. This is why it is called a stale closure.

Closures in React

We use closures in React all the time, but we don’t realize it.

Let’s take a look at this example:

const Counter = () => {
  const [count, setCount] = useState(0);
  const increment = () => setCount(count + 1);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

Components are just functions. So, when we call the Counter component, a new scope is created. This scope contains the count variable and the increment function. The increment function is a closure. It has access to the count variable, even after the Counter component has returned.

useCallback & useMemo

The useCallback hook is used to memoize functions. Every time we use useCallback , we create a closure, and the function that we pass to it is cached.

That dependency array is used to determine whether the function should be memoized or not. This is how we escape the stale closure trap for useCallback. Same thing for useMemo.

Refs

in the following example , We want onClick function that is stable between re- renders but also has access to the latest state without re-creating itself.

You can escape the stale closure in some useRef use cases by using the ref.current property. Let’s take a look at this example:

const Form = () => {
  const [value, setValue] = useState();
  const ref = useRef();
  useEffect(() => {
    ref.current = () => {
      // will be latest
      console.log(value);
    };
  });
  const onClick = useCallback(() => {
    // will be latest
    ref.current?.();
  }, []);
  return (
    <>
      <input
        type="text"
        value={value}
        onChange={e => setValue(e.target.value)}
      />
      <HeavyComponentMemo title="Welcome closures" onClick={onClick} />
    </>
  );
};

Notice how onClick is stable between re-renders but also has access to the latest state without re-creating itself.

Summary

This was a lot of mental gymnastics, but it’s worth it. If you want to learn more about closures, I highly recommend you to read the book You Don’t Know JS: Scope & Closures.

If you have any suggestions or questions, feel free to reach out to me on Twitter or LinkedIn.

P.S. this post is inspired by the book, but it’s not a summary of it. I’m just sharing what I learned from it. If you want to learn more about React, I highly recommend you to read it.