DEV Community

Ganesh R
Ganesh R

Posted on • Originally published at ganes.dev

5 1

Stale State in React Hooks

This post is originally published here
https://ganes.dev/stale-state-in-react-hooks

If you have been using React Hooks for some time or if you're new to using React hooks, one of the most common (or) confusing problems you face is that you sometimes the stale value of the state used in the application. Let us understand how to solve this problem.

Using values in useEffect with empty dependency array

Consider this code

function App() {
  const [title, setTitle] = useState('initial Title');

  useEffect(() => {
    document.title = title;
  }, []);

  return (
    <>
      <button
        onClick={() => {
          setTitle('New Title');
        }}>
        Change Title
      </button>
    </>
  );
}

Here the title of the document is Initial Title. And we expect the the document title to change when Change Title button is clicked.

But, that does not happen here. This is because the useEffect expects the dependencies used inside the effect to be passed as second argument. Since we use title inside useEffect we need to pass it as dependency. So, our above code changes to this.

// Rest of the part remains the same

useEffect(() => {
  document.title = title;
}, [title]);

Note: This will also work, if we pass no second argument in useEffect. But the that will run on every render. And passing all dependicies used inside useEffect is considered good
practice and also good for performance.

Using values inside callback passed to useEffect

This is a little subtle than the previous bug. Might be a little harder to debug.

function App() {
  const [cond, setCond] = useState(false)

  const someCallback = () => {
    if (cond) {
      alert('hello')
    }
  }

  useEffect(() => {
    someCallback()
  }, [])

  return (
    <>
      <button
        onClick={() => {
          setCond(c => !c)
        }}
      >
        Change State
      </button>
    </>
  )
}
}

In the above example, we are using the state value inside the callback passed to the useEffect. We expect the our component to alert hello everytime the condition becomes true.

But that does not happen here. This is because our initial value of cond is captured inside the callback passed to useEffect and does not get updated in subsequent renders.

Since, we are using this callback inside useEffect we need to tell React when the callback updates. In order to do that we need to wrap our callback in useCallback hook and pass the dependencies used inside the callback as second argument and then pass the callback to useEffect and list the callback as a dependency. So, our above code changes to this

// Rest of the part remains the same

const someCallback = useCallback(() => {
  if (cond) {
    alert('hello');
  }
}, [cond]);

useEffect(() => {
  someCallback();
}, [someCallback]);

To Avoid these kind of problems, I suggest to use the official eslint-plugin from the React team.

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay