Home > Software design >  Understanding PrevState in React
Understanding PrevState in React

Time:10-08

I am new to React and this might be a silly doubt. But I wasn't able to find the answer for this. When using state in React, to keep the display updated we use PrevState. For example:

const [state, setState] = useState(0);
setState(prevState => prevState  1);

I understand that prevState updates the state by 1 and keeps the UI up to date with the state value.

However, how is prevState assigned the value of the previous state. Isn't it just the name of the parameter of the arrow function? So how is this name assumed to be the previous state value? In forEach loop the parameter represents an element of the list. But here, how is the parameter PrevState being assigned the value?

CodePudding user response:

Internally, React keeps an array of all states for a given instance of a component. For example, if you have

const comp = () => {
  const [state, setState] = useState(0);
  const clickHandler = () => setState(prevState => prevState  1);
  return <button onClick={clickHandler}>click</button>
};

Then, because there's only one state for the component, on mount, React will have an array of one item that it stores internally:

[0]

When useState is called, the value that React currently has in its internal array for that state gets returned. So, with the above example, when you click, the state setter will tell React to update its internal state to:

[1]

After which the component re-renders due to the state change, so the state value returned by useState that re-render is 1.

When you use the callback form of the state setter, eg

setState(prevState => prevState  1);

The callback's parameter comes directly from React's internals. This can sometimes be more reliable than relying on the value in the outer state identifier, because the outer state value will be out of date if you previously set the same state without waiting for a re-render - for example, notice how the below increments the state by only 1 each click, rather than by 2.

const App = () => {
    const [count, setCount] = React.useState(0);
    const clickHandler = () => {
      setCount(count   1);
      setCount(count   1);
    };
    return (
      <div>
        {count}
        <button onClick={clickHandler}>click</button>
      </div>
    );
};

ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div class='react'></div>

In contrast, if you use the callback version, as soon as you call the first state setter, React's internals for the state will be updated - and then the second time you call the state setter, the parameter passed will be the newly updated value React has in state for it:

const App = () => {
    const [count, setCount] = React.useState(0);
    const clickHandler = () => {
      setCount(count => count   1);
      setCount(count => count   1);
    };
    return (
      <div>
        {count}
        <button onClick={clickHandler}>click</button>
      </div>
    );
};

ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div class='react'></div>

The callback approach is only needed if you have the possibility of a stale closure. If the value in state has no chance of being stale, then there's no need for the callback, and simply doing

setCount(count   1);

will work fine.

CodePudding user response:

how is prevState assigned the value of the previous state. Isn't it just the name of the parameter of the arrow function? So how is this name assumed to be the previous state value?

prevState is as you say just the name of the parameter of the function. There is nothing else we gain from the name prevState, apart from an understanding that prevState will be the old value of the state after that part of your component state is finally updated, this is why it is referred to as the previous state.

IMO, it is better terminology to use oldState rather than prevState, when updating state within the callback.

Proving that oldState === state within setState

Remember that you can call setState with just a new value for the state instead of a callback. So the following will be equivalent to what you currently have:

setState(state   1);

If we are to assume that the above use of setState is the correct way to add 1 to the current state (and yes it is correct), then we are safe to assume that in the use of setState like this:

setState(oldState => oldState  1);

oldState and state have to be the same.

Since we cannot use the name state within the setState callback, we can create an alias called currentState and we now have:

setState(currentState => currentState  1);

Therefore, oldState === state,

QED.

  • Related