0

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>
    )
  }

2 Answers 2

1

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
});
Sign up to request clarification or add additional context in comments.

2 Comments

Ilya, what do you think of my current working version? Is that worse/better compared to what you are suggesting?
If the order of operations - setState() and fetchingAPI() - is not important, (they're independent), then your workaround fits. If there are or may rise some side effect e.g. race conditions, then it's better to call fetchingAPI() in a callback passed to setState() to ensure the order of execution.
0

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")
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.