Some insights of useState, useEffect and stale closures

Some insights of useState, useEffect and stale closures

Today, I am going to tell you some insights into some major React hooks (useState, useEffect, useContext) that you have already used in your day-to-day life if you are developing React applications using Functional Components.

Prerequisite -> I am assuming that you have basic concepts and hands-on in React Applications built with Functional components and React hooks.

  • Before starting, I want to make sure that you understand the state in React.

    what is a state

    => State of a component is an object that holds some information that may change over the lifetime of the element. We should always try to make our state as simple as possible and minimize the number of stateful variables in react.

If you are not getting then in simple words you can consider the state as a variable based on which React again paints the screen or rerender the component.

What is UseState

  • Now you will ask What is useState? useState is a hook that helps us to make state variables in React Functional components.

It returns an array where index 0 is having the state variable and index 1 will have the setter function.

So when we are setting new references in the setter function React will rerender the component and make a new reference for the array means the state variable and setter function will have new references and here is the part I was so confused about and spent a lot of time to understand it while learning React.

If you are not able to understand till now I can explain with a different analogy. Let's refer to some chunk of code for this.

import { useState } from "react";
import "./styles.css";

export default function App() {
  const [counter, setCounter] = useState(0);

  return (
    <div className="App">
      <button onClick={() => setCounter(counter + 1)}>+</button>
      <p>Counter : {counter}</p>
    </div>
  );
}

So at the initial state suppose the useState array reference is A0 and the counter value is 0. After when we are clicking on + button , react rerenders the whole components and useState creates a new array with a new reference (guess it as B0) with a new updated useState value i.e. 1.

The previous array reference will get garbage collected.

Till now it feels easy right??

Let's dive deeper into it=>

I hope everyone knows about useEffect, in short, which hook runs after each render. For more details please refer MDN docs.

Guess the output in console=> (Please see the code part only don't see console)

Maybe according to you the output will be 0 after each 1 sec, now guess what will happen when we click on the + button. Will print it 1 in console??

ohh why it is printing 0 even though we are updating the counter.

So here the concept of Stale closures comes into play.

What is a closure?

A closure is a concept of remembering itself and its lexical scope. Please refer MDN docs to get more details.

Basically a closure is remembering the reference of some variable present in the same scope or its lexical scopes.

So here what is happening the counter got updated but with a new reference as useState creates a new reference after every render. The counter inside useEffect still refers to the previous old counter variable, that's why it is printing 0 even after incrementing several times the counter.

Now you can ask me if we want the updated counter value inside useEffect how to fix this=> Basically what is happening here useEffect runs at the first time only as dependency array is empty. So we have to make sure useEffect runs every after counter changes. Then we have to just put counter inside the dependency array.

I am putting the final code here

Thank you, folks, please comment here and give your valuable feedback