React can update state during render

After years of using React, it still packs a surprise. Did you know you can update state while rendering?

Yep. From the docs:

If you're like me, you wouldn't think to do this. You'd go with a useEffect instead.

Updating state during render is unintuitive because every setState triggers a re-render, but you're rendering right now. So how can you re-render while rendering without breaking something?

The clue comes in a different part of the docs, in the setState caveats section:

Calling the set function during rendering is only allowed from within the currently rendering component. React will discard its output and immediately attempt to render it again with the new state. This pattern is rarely needed, but you can use it to store information from the previous renders.

It's a performance and UX optimization of sorts. Here's the difference.

With useEffect

A counter component that resets to a new number when props change.

const Comp = ({ number }) => {
  const [realNumber, setRealNumber] = useState(number)

  useEffect(() => {
    setRealNumber(number)
  }, [number])

  // ...
}
  1. Component renders with value in state
  2. Effect runs
  3. State updates
  4. Component re-renders with new state

You get a flash of stale state.

With update during render

A counter component that resets to a new number when props change.

const Comp = ({ number }) => {
  const [realNumber, setRealNumber] = useState(number)
  const [prevProp, setPrevProp] = useState(number)

  if (prevProp !== number) {
    setRealNumber(number)
    setPrevProp(number)
  }

  // ...
}
  1. Component starts rendering
  2. State updates
  3. React bails and re-starts render
  4. Component renders with new state

No flash of stale state, fewer spooky interaction effects between various useEffect, a more stable experience for both you and the user. Yay.

Yes it does work

Here's a Codesandbox. I had to prove to myself that this works. Going to be useful in a few special cases like updating default values for form fields and menus that hide on scroll.

Cheers,
~Swizec