3

Normally when we need to update a state in a functional component, we do something like this:

function Example() {
    const [count, setCount] = React.useState(0);
    return (<div><p>You clicked {count} times</p>
                 <button onClick={() => setCount(count + 1)}>
                    Click me
                 </button>
            </div>);
}

When and why will we ever need to use the functional update form?

function Example() {
    const [count, setCount] = React.useState(0);
    return (<div><p>You clicked {count} times</p>
                 <button onClick={() => setCount(c=>c + 1)}>
                    Click me
                 </button>
            </div>);
}
3
  • 4
    When you know that newState is dependent on the previous state. Simple
    – DecPK
    Commented Aug 16, 2021 at 3:57
  • @DecPK Is it safe to say that for operations such as incrementing the state variable by x, we should always do setState(count => count + 1) instead of setState(count+1)
    – Murtuza
    Commented Jan 25 at 5:19
  • 1
    @Murtuza Not necessary when you are using functional update you will get the latest value at that time. and when you use just count + 1 then count will be taken from closure.
    – DecPK
    Commented Jan 26 at 7:48

6 Answers 6

4

Use the function form when the setter may close over an old state value.

For example, if an async request is initiated, and you want to update state after that's done, the request that was made will have scope of the state as it was at the beginning of the request, which may not be the same as the most up-to-date render state.

You may also need to use the function form if the same state value was just updated, eg

setValue(value + 1);
// complicated logic here
if (someCondition) {
  setValue(value => value + 1);
}

because the second call of setValue closes over an old value.

2
  • I tried calling setCount(x=>(x+1)) twice in a row but it still incremented by 1 only. Commented Aug 16, 2021 at 4:07
  • @ChongLipPhang That should not happen. It should increment by two. Commented Aug 16, 2021 at 4:08
1

State Updates May Be Asynchronous:

https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

useState is the same as setState in this condition.

1

You can see the different when call set state twice:

<button
  onClick={() => {
    setCount(count + 1);
    setCount(count + 1);
  }}
></button>;

<button
  onClick={() => {
    setCount(c => (c + 1));
    setCount(c => (c + 1));
  }}
></button>;
2
  • Can we say that the functional update form is synchronous? Commented Aug 16, 2021 at 4:12
  • 1
    No. functional update always get newest value of stae
    – Viet
    Commented Aug 16, 2021 at 4:13
1

There are other use cases too. For example, when you call useState inside an effect. If new state is dependent on old state, this might cause an infinite loop.

useEffect(() => {
  setCounter(counter + 1);
}, [counter]);

You can avoid this by using functional updates:

useEffect(() => {
  setCounter(old => old + 1);
}, []);
2
  • I wish I can undo the upvote... Doesn't useEffect(() => {setCounter(counter+ 1);}, []); achieve the same thing? Commented Aug 16, 2021 at 6:30
  • @ChongLipPhang removing a dependency from deps array is against react-hooks linting rules (exhaustive-deps). Also, it won't update counter correctly. This is just a basic example. There are more complicated, real-world cases when you should update states in useEffect hooks.
    – glinda93
    Commented Aug 16, 2021 at 6:43
1

The functional update form also allows the update function to be passed to its children while still having access to the parent’s state.

function MyButton(props){
     // return <button onClick={()=>props.onClick(count+1)}>+1</button>;  // error as count is not exposed here
     return <button onClick={()=>props.onClick(n=>(n+1))}>+1</button>;
}
function Example() {
   const [count, setCount] = React.useState(0);
   return (<div><p>Counter: {count}</p>
                <MyButton onClick={setCount}/>
           </div>);
}
ReactDOM.render(<Example/>,document.querySelector("div"));
0

According to this: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

The the functional update form make sure that the previous state that you take reference from is the latest / finalized version when there might be multiple setState hook (which is asynchronous) called (for example, if the user spam click on the button)

And also due to its async nature, the state will not be updated right away within a function, for e.g:

func() {
 console.log(counter) // counter = 1
 setCounter(counter => counter + 1) // counter = 1
 console.log(counter) // counter = 1
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.