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
Difference Between Client-Side and Server-side Programming
Communication Between Tabs or Windows
"Submit Is Not a Function" Error in JavaScript
What Does "./" (Dot Slash) Refer to in Terms of an HTML File Path Location
Contenteditable, Set Caret At the End of the Text (Cross-Browser)
How to Apply CSS to Half of a Character
How to Stop a Web Page from Scrolling to the Top When a Link Is Clicked That Triggers JavaScript
How to Create a Link Using JavaScript
Disable Form Auto Submit on Button Click
How to Pass Variables and Data from PHP to JavaScript
Include Another HTML File in a HTML File
Get Cursor Position (In Characters) Within a Text Input Field
Detecting Input Change in Jquery
How to Get the Raw Value an ≪Input Type="Number"≫ Field
How to Simulate a Click by Using X,Y Coordinates in JavaScript