I’m reading Forms section of 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?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
From React’s documentation:
setState()
does not immediately mutatethis.state
but creates a
pending state transition. Accessingthis.state
after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls tosetState
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); });
Method 2
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 setState
s 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(); } }
Method 3
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); }
Method 4
Watch out the react lifecycle methods!
- http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
- https://reactjs.org/docs/react-component.html
I worked for several hours to find out that getDerivedStateFromProps
will be called after every setState()
.
😂
Method 5
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}`); }
Method 6
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:
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.
Method 7
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); });
Method 8
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.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0