Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
July 8, 2020 06:46 am GMT

We don't know how React state hook works

If you prefer watching a video, here is the Youtube version of this article

This article is about:

  • When is the state updated
  • The update queue and lazy computation
  • Batching
  • useState vs. useReducer
  • Performance optimizations
    • eagerly computing state updates
    • shallow rendering and bailing out
  • Will the updater function always run?

When is the state updated?

Look at this code:

const MyComp = () => {  const [counter, setCounter] = useState(0);  onClick = () => setCounter(prev => prev + 1);  return <button onClick={onClick}>Click me</button>}

What would you imagine happen after the button is clicked and setCounter is called? Is it this:

  • React calls the updater function (prev => prev + 1)
  • Updates the hook's state (= 1)
  • Re-renders component
  • Render function calls useState and gets updated state (== 1)

If this is what you imagine - then you are wrong. I was also wrong about this, until I did some experiments and looked inside the hooks source code.

The update queue and lazy computation

It turns out, every hook has an update queue. When you call the setState function, React doesn't call the updater function immediately, but saves it inside the queue, and schedules a re-render.

There might be more updates after this one, to this hook, other hooks, or even hooks in other components in the tree.
There might be a Redux action that causes updates in many different places in the tree. All of these updates are queued - nothing is computed yet.

Finally, React re-renders all components that were scheduled to be rendered, top-down. But the state updates are still not performed.

It's only when useState actually runs, during the render function, that React runs each action in the queue, updates the final state, and returns it back.

This is called lazy computation - React will calculate the new state only when it actually needs it.

To summarize, what happens is this (simplified):

  • React queue's an action (our updater function) for this hook
  • Schedules a re-render to the component
  • When render actually runs (more about this later):
    • Render runs the useState call
    • Only then, during useState, React goes over the update queue and invokes each action, and saves the final result in the hook's state (in our case - it will be 1)
    • useState returns 1

Batching

So when does React say: "OK, enough queueing updates and scheduling renders, let me do my job now"? How does it know we're done updating?

Whenever there's an event handler (onClick, onKeyPress, etc.) React runs the provided callback inside a batch.
The batch is synchronous, it runs the callback, and then flushes all the renders that were scheduled:

const MyComp = () => {  const [counter, setCounter] = useState(0);  onClick = () => { // batch starts    setCounter(prev => prev + 1); // schedule render    setCounter(prev => prev + 1); // schedule render  } // only here the render will run  return <button onClick={onClick}>Click me</button>}

What if you have any async code inside the callback? That will be run outside the batch. In this case, React will immediately start the render phase, and not schedule it for later:

const MyComp = () => {  const [counter, setCounter] = useState(0);  onClick = async () => {    await fetch(...); // batch already finished    setCounter(prev => prev + 1); // render immediately    setCounter(prev => prev + 1); // render immediately  }  return <button onClick={onClick}>Click me</button>}

State is Reducer

I mentioned earlier that "React runs each action in the queue". Who said anything about an action?

It turns out, under the hood, useState is simply useReducer with the following basicStateReducer:

function basicStateReducer(state, action) {  return typeof action === 'function' ? action(state) : action;}

So, our setCounter function is actually dispatch, and whatever you send to it (a value or an updater function) is the action.

Everything that we said about useState is valid for useReducer, since they both use the same mechanism beind the scenes.

Performance optimizations

You might think - if React computes the new state during render time, how can it bail out of render if the state didn't change? It's a chicken and egg problem.

There are 2 parts to this answer.

There's actually another step to the process. In some cases, when React knows that it can avoid re-render, it will eagerly compute the action. This means that it will run it immediately, check if the result is different than the previous state, and if it's equal - it will not schedule a re-render.

The second scenario, is when React is not able to eagerly invoke the action, but during render React figures out nothing changed, and all state hooks returned the same result. The React team explains this best inside their docs:

Bailing out of a state update
If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the Object.is comparison algorithm.)

Note that React may still need to render that specific component again before bailing out. That shouldnt be a concern because React wont unnecessarily go deeper into the tree. If youre doing expensive calculations while rendering, you can optimize them with useMemo.

https://reactjs.org/docs/hooks-reference.html#bailing-out-of-a-state-update

Put shortly, React may run the render function and stop there if nothing changed, and won't really re-render the component and its children.

Will the updater function always run?

The answer is no. For example, if there's any exception that will prevent the render function from running, or stop it in the middle, we won't get to the useState call, and won't run the update queue.

Another option, is that during the next render phase our component is unmounted (for example if some flag changed inside the parent component). Meaning the render function won't even run, let alone the useState expression.

Learned something new? Found any mistakes?

Let me know in the comments section below


Original Link: https://dev.to/adamklein/we-don-t-know-how-react-state-hook-works-1lp8

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To