When to Use Functional Setstate

When to use functional setState

First things first, in your case the two syntaxes are entirely different, what you might be looking for is the difference between

this.setState({pictures: this.state.picture.concat(pics)})

and

this.setState(prevState => ({
pictures: prevState.pictures.concat(pics)
}))

To understand why the second method is a preferred one,you need to understand what React does with setState() internally.

React will first merge the object you passed to setState() into the current state. Then it will start that reconciliation thing. Because of the calling setState() might not immediately update your state.

React may batch multiple setState() calls into a single update for better performance.

Consider the simple case, to understand this, in your function you might call setState() more than once like:

myFunction = () => {

...
this.setState({pictures: this.state.picture.concat(pics1)})
this.setState({pictures: this.state.picture.concat(pics1)})
this.setState({pictures: this.state.picture.concat(pics1)})

...
}

which isn't a valid use case in a simple app but as the app gets complex, multiple setState() calls may be happening from multiple places, doing the same thing.

So now to perform an efficient update, React does the batching thing by extracting all the objects passed to each setState() call, merges them together to form a single object, then uses that single object to do setState(). According to the setState() documentation:

This form of setState() is also asynchronous, and multiple calls during the same cycle may be batched together. For example, if you attempt to increment an item quantity more than once in the same cycle, that will result in the equivalent of:

Object.assign(
previousState,
{quantity: state.quantity + 1},
{quantity: state.quantity + 1},
...
)

Subsequent calls will override values from previous calls in the same cycle, so the quantity will only be incremented once. If the next state depends on the current state, we recommend using the updater function form, instead:

this.setState((state) => {
return {quantity: state.quantity + 1};
});

For more detail, see:

  • State and Lifecycle guide
  • In depth: When and why are setState() calls batched?
  • In depth: Why isn’t this.state updated immediately?

setState() - Other APIs - React.Component – React.

So if any of the objects contains the same key, the value of the key of the last object with same key is stored. And hence the update only happens once with the last value.

Demo Codesandbox

Do we still need functional setState way in react hooks?

Yes, the behavior is similar.

React is batching the updates calls.
When Writing:

const handleClick = () => setCount(count + 1)
handleClick()
handleClick()
handleClick()

the count in state will be 1

When Writing:

const handleClick = () =>
setCount(prevCount => {
return prevCount + 1;
});
handleClick()
handleClick()
handleClick()

the count in state will be 3

When to use an object in `setState` instead of using functional `setState`?

The norm is to use an object, it's also less code and easier to read. The only reason u want to use a function setState is when u need to access the previous state.

consider the following example

// you have a switch state

state = {
checked: false
}

// .. and later in an onChange method

onChange = () => {
const {checked} = this.state;
// this is problematic because react works in an async fashion, so it could be that when this function was called checked was false, however it was only executed later when checked was actually true
this.setState({checked: !checked}

// on the other hand this form is safe, even if things were happening async, you are safe because you act on the most updated state
this.setState(prvState => {...prvState, checked: !prvState.checked})
}

What is the use of functional syntax of setState in react functional components?

They are not the same, if your update depends on a previous value found in the state, then you should use the functional form. If you don't use the functional form in this case then your code will break sometime.

Why does it break and when

React functional components are just closures, the state value that you have in the closure might be outdated - what does this mean is that the value inside the closure does not match the value that is in React state for that component, this could happen in the following cases:

1- async operations (In this example click slow add, and then click multiple times on the add button, you will later see that the state was reseted to what was inside the closure when the slow add button was clicked)

const App = () => {
const [counter, setCounter] = useState(0);

return (
<>
<p>counter {counter} </p>
<button
onClick={() => {
setCounter(counter + 1);
}}
>
immediately add
</button>
<button
onClick={() => {
setTimeout(() => setCounter(counter + 1), 1000);
}}
>
Add
</button>
</>
);
};

2- When you call the update function multiple times in the same closure

const App = () => {
const [counter, setCounter] = useState(0);

return (
<>
<p>counter {counter} </p>
<button
onClick={() => {
setCounter(counter + 1);
setCounter(counter + 1);
}}
>
Add twice
</button>

</>
);
}

When to use functional update form of useState() hook, eg. setX(x=x+1)

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.

React - Functional setState (previous state) different from new updated value?

If your new state is calculated based on any value which is already in state - then you should use the second form of setState where you use a function:

this.setState(prevState => ({
ninjas: [...prevState.ninjas, newNinja]
}));

or even:

this.setState(({ninjas}) => ({
ninjas: [...ninjas, newNinja]
}));

It's due to state changes are asynchronous and could be batched due to performance reasons.

If you change the state using some variable which is not based on any value from state - you're free to use a simple version:

this.setState({
answer: 42
});

Regarding your

since we use a new array each time in the first example

indeed you create a new array each time, but you create it using some set of items which can be not relevant by that time when actual state change will be performed by React under the hood.



Related Topics



Leave a reply



Submit