score:427
I work on React.
TLDR:
But can you trust React to update the state in the same order as setState is called for
- the same component?
Yes.
- different components?
Yes.
The order of updates is always respected. Whether you see an intermediate state "between" them or not depends on whether you're inside in a batch or not.
In React 17 and earlier, only updates inside React event handlers are batched by default. There is an unstable API to force batching outside of event handlers for rare cases when you need it.
Starting from React 18, React batches all updates by default. Note that React will never batch updates from two different intentional events (like clicks or typing) so, for example, two different button clicks will never get batched. In the rare cases that batching is not desirable, you can use flushSync
.
The key to understanding this is that no matter how many setState()
calls in how many components you do inside a React event handler, they will produce only a single re-render at the end of the event. This is crucial for good performance in large applications because if Child
and Parent
each call setState()
when handling a click event, you don't want to re-render the Child
twice.
In both of your examples, setState()
calls happen inside a React event handler. Therefore they are always flushed together at the end of the event (and you don't see the intermediate state).
The updates are always shallowly merged in the order they occur. So if the first update is {a: 10}
, the second is {b: 20}
, and the third is {a: 30}
, the rendered state will be {a: 30, b: 20}
. The more recent update to the same state key (e.g. like a
in my example) always "wins".
The this.state
object is updated when we re-render the UI at the end of the batch. So if you need to update state based on a previous state (such as incrementing a counter), you should use the functional setState(fn)
version that gives you the previous state, instead of reading from this.state
. If you're curious about the reasoning for this, I explained it in depth in this comment.
In your example, we wouldn't see the "intermediate state" because we are inside a React event handler where batching is enabled (because React "knows" when we're exiting that event).
However, both in React 17 and earlier versions, there was no batching by default outside of React event handlers. So if in your example we had an AJAX response handler instead of handleClick
, each setState()
would be processed immediately as it happens. In this case, yes, you would see an intermediate state in React 17 and earlier:
promise.then(() => {
// We're not in an event handler, so these are flushed separately.
this.setState({a: true}); // Re-renders with {a: true, b: false }
this.setState({b: true}); // Re-renders with {a: true, b: true }
this.props.setParentState(); // Re-renders the parent
});
We realize it's inconvenient that the behavior is different depending on whether you're in an event handler or not. In React 18, this is no longer necessary, but before that, there was an API you can use to force batching:
promise.then(() => {
// Forces batching
ReactDOM.unstable_batchedUpdates(() => {
this.setState({a: true}); // Doesn't re-render yet
this.setState({b: true}); // Doesn't re-render yet
this.props.setParentState(); // Doesn't re-render yet
});
// When we exit unstable_batchedUpdates, re-renders once
});
Internally React event handlers are all being wrapped in unstable_batchedUpdates
which is why they're batched by default. Note that wrapping an update in unstable_batchedUpdates
twice has no effect. The updates are flushed when we exit the outermost unstable_batchedUpdates
call.
That API is "unstable" in the sense that we will eventually remove it in some major version after 18 (either 19 or further). You safely rely on it until React 18 if you need to force batching in some cases outside of React event handlers. With React 18, you can remove it because it doesn't have any effect anymore.
To sum up, this is a confusing topic because React used to only batch inside event handlers by default. But the solution is not to batch less, it's to batch more by default. That's what we're doing in React 18.
score:3
as in doc
setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state. This is the primary method you use to update the user interface in response to event handlers and server responses.
it will preform the change as in queue (FIFO : First In First Out) the first call will be first to preform
score:4
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},
...
)
score:9
This is actually a quite interesting question but the answer shouldn't be too complicated. There is this great article on medium that has an answer.
1) If you do this
this.setState({ a: true });
this.setState({ b: true });
I don't think that there will be a situation where a
will be true
and b
will be false
because of batching.
However, if b
is dependent on a
then there indeed might be a situation where you wouldn't get the expected state.
// assuming this.state = { value: 0 };
this.setState({ value: this.state.value + 1});
this.setState({ value: this.state.value + 1});
this.setState({ value: this.state.value + 1});
After all the above calls are processed this.state.value
will be 1, not 3 like you would expect.
This is mentioned in the article: setState accepts a function as its parameter
// assuming this.state = { value: 0 };
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));
This will give us this.state.value === 3
Source: stackoverflow.com
Related Query
- Does React keep the order for state updates?
- How does React re-use child components / keep the state of child components when re-rendering the parent component?
- Why does React render component for the second time after setting the state to the same value?
- How does the state updates of objects in React work accurately
- React cannot set state inside useEffect does not update state in the current cycle but updates state in the next cycle. What could be causing this?
- Why does React protected route works for the 2nd time with Recoil state
- How can I update react state quick enough in order for the hover effect to feel responsive?
- Why does not the background color get applied from props using state for a styled component div using react and typescript?
- Why does calling react setState method not mutate the state immediately?
- Does react re-render the component if it receives the same value in state
- React - weird behavior when useState hook sets state for the first time vs subsequent times
- React w/ TypeScript: why is the type for the state lost when useState with a callback as the updater
- If the props for a child component are unchanged, does React still re-render it?
- Why getDerivedStateFromProps does not allow to re-render with the state update? Not a problem for componentWillReceiveProps - ReactJS
- React leaflet center attribute does not change when the center state changes
- Why does React have to use setState for state update?
- React hooks - useState() is not re-rendering the UI with the new state updates
- React does not refresh after an update on the state (an array of objects)
- bind(): You are binding a component method to the component. React does this for you automatically?
- ReactJS - How can I implement pagination for react with keep state when route change?
- Better way to use useState hook for setting the boolean state in React
- How to keep the React state in sync with MySQL database
- passing a type argument for the props of a react component for a higher order component
- How to pass the initial state as props for my Higher Order Component?
- How to know if all the setState updates have been applied to the state in a React component?
- How does React State Hook gets the state variable name from the ES6 Destructuring Assignment
- useState react hook does not set the array state
- Why does React re-render differently whether the state is an object vs a string?
- Do React state updates occur in order when used like an FSM and useEffect?
- Is it a bad practice to keep strong ui related state inside of the react component?
More Query from same tag
- react with IE11 is not working, displaying blank screen
- Redux state is being updated without dispatching any action
- react search filter with select option
- Recharts: Turn off background hover for bar charts
- Webpack loader configuration to load css and sass files for React JS
- Dynamically spread setState doesn't work on ternary operator spread
- more components connected to redux or less? react
- React Rendering Dynamic Inner Components
- How to add favicon to react application browser tab (duplicate)
- Error Importing with scss @import in storybook
- Trying to build React Component that can iterate over a nested data structure and produce styled HTML elements based on type
- Material-UI Dialog style action button
- Trigger event on an injected element in the DOM in React
- How can I fix 503 (service unavailable)?
- Only render specific component in a big react project
- dynamically change the basename in react BrowserRouter with API call
- MaterialUI Spinning loader help needed for React/Redux app
- How to repeatedly call a function while mouse down?
- How to handle bad request in fetch()
- Is there any solve to communicate Parent React Router and Child React router?
- redux-saga, TypeError: (0 , _effects.takeLatest) is not a function?
- React passing props (react-to-print)
- setTimeout() function called before duration of timeout?
- React - set accessibility focus to an element
- Unable to install from forked github repo using npm
- Creating Parallax effect using setState
- How to integrate the Twitter widget into Reactjs?
- `this` not scoped in React Component event handlers
- Images not loading in React app when deployed on Heroku
- Interpolate a dynamic HTML element in React