What Happens When Using This.Setstate Multiple Times in React Component

What happens when using this.setState multiple times in React component?

React batches state updates that occur in event handlers and lifecycle methods. Thus, if you update state multiple times in a <div onClick /> handler, React will wait for event handling to finish before re-rendering.

To be clear, this only works in React-controlled synthetic event handlers and lifecycle methods. State updates are not batched in AJAX and setTimeout event handlers, for example.

UPDATE

With the release of React v18.0, state updates within non-React events (promises, setTimeout etc.) are also batched by default.

Ref - https://reactjs.org/blog/2022/03/29/react-v18.html#new-feature-automatic-batching

Why does state not update correctly when calling setState() multiple times

State Updates May Be Asynchronous

Quoted from the reactjs docs about state updates:

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

Because this.props and this.state may be updated asynchronously, you
should not rely on their values for calculating the next state
.

Your second call to addMessage() uses this.state to set the new state. But because setState() is asynchronous the state my not have been updated yet which leads to your second call overriding your first one:

addMessage(message) {

// here you access this.state which hasn't been updated yet in your second call
let messages = this.state.messages.slice();

messages.push({text: message});
this.setState({messages: messages});
}

To fix it, use a second form of setState() that accepts a function
rather than an object. That function will receive the previous state
as the first argument, and the props at the time the update is applied
as the second argument:

Use an arrow function that gets passed the previous state including changes of preceding calls to setState()to calculate the new state. That will fix your issue.

Using es6 spread syntax:

this.setState((prevState, props) => ({
messages: [...prevState.messages, {text: message}]
}));

Using slice():

this.setState((prevState, props) => {
let messages = prevState.messages.slice();
messages.push({text:message});
return {messages};
});

setState being called multiple times after await

This happens because as this answer states React tries to batch setState calls and process them together when it can. But this is not the case with asynchronous computations because React (and anybody in general) can't predict and reproduce the order of setState's called asynchronously.

So in your case it fallbacks to just updating state 3 times.

React setState called multiple times on the same state object

If you are writing ES2015, you can use the spread operator to copy the whole object and just modify one of it's properties:

setOptionAVisibility (onOff, optionID) {
this.setState({
optionA:
{
...this.state.optionA,
option_id: optionID,
on: onOff
}
})
}

Can be very useful when modifying single properties of complex objects on the state tree.

Render Once for Multiple set States using useState hook in React

You can use your states as an object and set them in one instead of separete states like

const [exampleState, setExampleState] = useState(
{fields: {
fieldOne: false,
fieldTwo: false
}
})

You can set in this way

    setExampleState({...exampleState, fields: {
fieldOne: true,
fieldTwo: true
},
})

Why is my component in React being called multiple times?

Because React will re-render when state change, if you want stop re-render, put your grabListings() inside useEffect() like this:

useEffect(() => {
grabListings();
},[])

Multiple set state within multiple api call inside componentDidMount in ReactJS

You should set state in a single update, updating both values at the same time. Otherwise you are instructing React to initiate two renders, the first would contain users value and an undefined value for banks (depending on your initial state declaration). This render would be quickly followed by a second pass, in which both state values users and banks would be defined.

The below example should work as required, in a single render.

Promise.all([
axios.get(<url1>),
axios.get(<url2>)
]).then(([res1, res2]) => {
// call setState here
const users = res1.data.users;
const banks = res2.data.banks;
this.setState({ users, banks });
});

On the other hand, if for some strange requirement you actually want two sequential renders you can use setState's done callback; example below.

this.setState({ users }, () => {
this.setState({ banks });
});

This will ensure the first render is complete before requesting a new render via setState.



Related Topics



Leave a reply



Submit