Reputation: 2380
I have a small react app. When selected dep/arr station changes it refetches the schedule data from the BART API.
For some reason with the old code (see below) it didn't work properly. I did setState
first then tried to use the new value of the depStation
, but it showed the previous value. So for instance let's say I reload the page the initial depState value is SFIA
. If I change the value to FRMT
then the console.log(selected)
shows FRMT
but the console.log(this.state.depStation)
still shows SFIA
. If I change it again to HAYW
the console.log(selected)
shows HAYW
but the console.log(this.state.depStation)
shows FRMT
. To make the app work I simply just used the selected
instead of the this.state.depStation
, but I guess this is not the best approach.
So I don't really understand why this.state.depStation
shows the prev data after calling this.setState({depStation: selected})
. Could sby explain me why this is happening?
Old version which was not working:
reRunFetching(selected, type) {
if (type === "depart") {
this.setState({depStation: selected});
console.log(selected); //Showing properly what I select
console.log(this.state.depStation); //For some reason the previously selected
}
else if (type === "arrive") {
this.setState({arrStation: selected});
}
this.fetchingAPI(this.state.depStation, this.state.arrStation)
}
New version. This is working fine, but I guess it's not the best solution:
reRunFetching(selected, type) {
if (type === "depart") {
this.fetchingAPI(selected, this.state.arrStation)
this.setState({depStation: selected});
console.log(selected, this.state.arrStation);
}
else if (type === "arrive") {
this.fetchingAPI(this.state.depStation, selected)
this.setState({arrStation: selected});
console.log(this.state.depStation, selected);
}
}
rest of index.js
class App extends Component {
constructor(props) {
super(props);
this.state = {
schedules: [],
depStation: DEP_STATION,
arrStation: ARR_STATION
};
this.fetchingAPI(this.state.depStation, this.state.arrStation)
}
fetchingAPI(departureStation, arrivalStation) {
fetch("http://api.bart.gov/api/sched.aspx?cmd=depart&orig=" + departureStation + "&dest=" + arrivalStation + "&date=now&key=MW9S-E7SL-26DU-VV8V&b=0&a=4&l=0")
.then(function(response) {
return response.text();})
.then((responseXML) => {
let tripsArray = this.parsingXML(responseXML);
this.setState({schedules: tripsArray});
})
.catch((error) => {
console.log(error);
});
}
render () {
return (
<div>
<div className="row">
<div className="col-md-5 col-md-offset-1 search-bar">
<SelectDepart onSelectedChange={selected => this.reRunFetching(selected, "depart")}/>
</div>
<div className="col-md-5 search-bar">
<SelectArrive onSelectedChange={selected => this.reRunFetching(selected, "arrive")}/>
</div>
</div>
<TimeTable schedules={this.state.schedules} />
</div>
)
}
Upvotes: 0
Views: 705
Reputation: 170
setState() is an asynchronous method which queue your state. so in order to access state value immidiately after setState you need to call a callback function as second argument to setState method, which will first set your state and after that will re render your view with updated state. below example will help you.
this.setState({
depStation: selected
}, () => {
// here you will get your depStation state value
console.log(this.state.depStation,"depStation value")
});
Upvotes: 0
Reputation: 204
setState() is an asynchronous non-blocking method which doesn't immediately set the new state, as you expect it to. As the official docs says:
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 need you can pass a callback a a second argument to setState() and it will be fired on state change:
this.setState({depStation: selected}, function() {
// some code
});
Upvotes: 1