Why calling react setState method doesn't mutate the state immediately?

JavascriptReactjsReact Jsx

Javascript Problem Overview


I'm reading Forms section of [tag:reactjs] documentation and just tried this code to demonstrate onChange usage (JSBIN).

var React= require('react');

var ControlledForm= React.createClass({
	getInitialState: function() {
		return {
			value: "initial value"
		};
	},

	handleChange: function(event) {
		console.log(this.state.value);
		this.setState({value: event.target.value});
		console.log(this.state.value);

	},

	render: function() {
		return (
			<input type="text" value={this.state.value} onChange={this.handleChange}/>
		);
	}
});

React.render(
	<ControlledForm/>,
  document.getElementById('mount')
);

When I update the <input/> value in the browser, the second console.log inside the handleChange callback prints the same value as the first console.log, Why I can't see the result of this.setState({value: event.target.value}) in the scope of handleChange callback?

Javascript Solutions


Solution 1 - Javascript

From React's documentation:

> setState() does not immediately mutate this.state but creates a > pending state transition. Accessing this.state after calling this > method can potentially return the existing value. There is no > guarantee of synchronous operation of calls to setState and calls may > be batched for performance gains.

If you want a function to be executed after the state change occurs, pass it in as a callback.

this.setState({value: event.target.value}, function () {
    console.log(this.state.value);
});

Solution 2 - Javascript

As mentioned in the React documentation, there is no guarantee of setState being fired synchronously, so your console.log may return the state prior to it updating.

Michael Parker mentions passing a callback within the setState. Another way to handle the logic after state change is via the componentDidUpdate lifecycle method, which is the method recommended in React docs.

> Generally we recommend using componentDidUpdate() for such logic instead.

This is particularly useful when there may be successive setStates fired, and you would like to fire the same function after every state change. Rather than adding a callback to each setState, you could place the function inside of the componentDidUpdate, with specific logic inside if necessary.

// example
componentDidUpdate(prevProps, prevState) {
  if (this.state.value > prevState.value) {
    this.foo();  
  }
}

Solution 3 - Javascript

You could try using ES7 async/await. For instance using your example:

handleChange: async function(event) {
    console.log(this.state.value);
    await this.setState({value: event.target.value});
    console.log(this.state.value);
}

Solution 4 - Javascript

Watch out the react lifecycle methods!

I worked for several hours to find out that getDerivedStateFromProps will be called after every setState().

Solution 5 - Javascript

async-await syntax works perfectly for something like the following...

changeStateFunction = () => {
  // Some Worker..

  this.setState((prevState) => ({
  year: funcHandleYear(),
  month: funcHandleMonth()
}));

goNextMonth = async () => {
  await this.changeStateFunction();
  const history = createBrowserHistory();
  history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}

goPrevMonth = async () => {
  await this.changeStateFunction();
  const history = createBrowserHistory();
  history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}

Solution 6 - Javascript

Accessing this.state after calling the setState method is not guaranteed to return the updated status due to the asynchronous nature of setState.

To guarantee an update after calling setState, there are two solutions you may pursue.

Solution 1: As mentioned in one of the above answers, put your code in the componentDidUpdate method

Solution 2: As mentioned in another of the above answers, pass your stuff as a callback

 this.setState({value: myValue}, function () {
    this.functionThatIsExecutedWhenStateIsUpdated();
});

It's important to note that these two solutions are not clearly interchangeable. The one cannot easily solve all the use-cases of the other. As a general rule, if you can, best practice says that solution 1 is preferred. But, there are use-cases where only solution 2 "more effectively" works such as the "update-my-view-and-post-my-data" use case. This use case goes like this:

After adding an item, say, "Add Schedule", I want to both add that item to a front-end list and immediately post the just-updated-list to the backend, as demonstrated in the concept below: enter image description here

If you dont do either solution, i.e. if you only say this in your code:

addToItemArray = () => { 
     this.setState{{ scheduledItemsArray: newObjectListWithMax}}   
     this.postData();
}

<button className="btn btn-secondary btn-block" onClick={this.addToItemArray}>Add Shedule</button>

... you will post the list excluding the "Delivery to Max" item, because the state wont be updated when you this.postData() (again, because its asynchronous).

If you utilise solution 1, you would make a POST after typing in every character in the Schedule Name textbox!

There are other ways aswell to cater for this use-case but solution 2 best conveys the intent when reading the code.

Given the ubiquitous nature of this use case in virtually every web app, the callback technique explained by Michael's answer is an indispensable piece of code in every developers toolkit.

Solution 7 - Javascript

> Simply putting - this.setState({data: value}) is asynchronous in > nature that means it moves out of the Call Stack and only comes back > to the Call Stack unless it is resolved.

Please read about Event Loop to have a clear picture about Asynchronous nature in JS and why it takes time to update -

> https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4

Hence -

    this.setState({data:value});
    console.log(this.state.data); // will give undefined or unupdated value

as it takes time to update. To achieve the above process -

    this.setState({data:value},function () {
     console.log(this.state.data);
    });

Solution 8 - Javascript

React bathces different set state calls so that it can determine what is the most optimal strategy for rerendering the website is going to be.

Imagine you have an application where you have a lot of different components. Perhaps, with one button click you are updating the state in multiple components, not just on the current one. In this case, React does not want to just completely isolate and do all those different updates independently.

React wants to figure out if it can stack all these updates together, maybe there is a more optimal way of updating these components so that it is more performant. This is what React is doing behind the scenes. As a result, set state call is asynchronous call.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSalah Eddine TaouriritView Question on Stackoverflow
Solution 1 - JavascriptMichael ParkerView Answer on Stackoverflow
Solution 2 - JavascriptYo WakitaView Answer on Stackoverflow
Solution 3 - JavascriptkurokiiruView Answer on Stackoverflow
Solution 4 - JavascriptyangsibaiView Answer on Stackoverflow
Solution 5 - JavascriptRitwikView Answer on Stackoverflow
Solution 6 - JavascriptDean PView Answer on Stackoverflow
Solution 7 - JavascriptVishal BishtView Answer on Stackoverflow
Solution 8 - JavascriptYilmazView Answer on Stackoverflow