0

In componentDidMount() I'm calling setQueryParams to set the query parameters into state which works fine.

 componentDidMount() {
    this.setQueryParams();

    console.log(this.state.query);

    $.getJSON(`/search?${this.state.query}`, (response) => {
      this.addDiveCenters(response.centers)
      this.buildFilters(response.filters, false)
    });

    console.log(this.state.query);
  }

However, after the setQueryParams() call, console.log(this.state.query) is returning '' (the default state). After the page loads I can run $r.state.query and everything is perfect,but for some reason I cannot access the state! I need the initial state to make an AJAX call. Right now, every single page loads to /search because this.state.query is blank.

 setQueryParams = (param = null, value = null) => {
    const query = this.state.query;
    // get current location
    const location = this.props.location.search;

    // Set params based on whether component mount or filter change
    const params = param ? new URLSearchParams(query) : new URLSearchParams(location);

    if (param) {
      params.set(param, value);
    }

    this.setState({
      query: params.toString()
    });

    console.log(this.state.query); // doesn't display anything!
  }
David Schumann
  • 13,380
  • 9
  • 75
  • 96
Yitzhak
  • 343
  • 1
  • 4
  • 16

4 Answers4

2

It will take some time because this.setState behaves asynchronous

this.setState has callback method which notify that state has been updated

 this.setState({
      query: params.toString()
    },() => { 
    // Do something here. 
  });

Read this

https://medium.com/@wereHamster/beware-react-setstate-is-asynchronous-ce87ef1a9cf3

Solution :-

componentDidMount() {

    this.setQueryParams(null, null, () => {

        console.log(this.state.query);

        $.getJSON(`/search?${this.state.query}`, (response) => {
            this.addDiveCenters(response.centers)
            this.buildFilters(response.filters, false)
        });

        console.log(this.state.query);
    });
}

Pass callback method in setQueryParams

 setQueryParams = (param = null, value = null,callback) => {
    const query = this.state.query;
    // get current location
    const location = this.props.location.search;

    // Set params based on whether component mount or filter change
    const params = param ? new URLSearchParams(query) : new URLSearchParams(location);

    if (param) {
      params.set(param, value);
    }

    this.setState({
      query: params.toString()
    },callback);

    console.log(this.state.query); // doesn't display anything!
  }
David Schumann
  • 13,380
  • 9
  • 75
  • 96
Nishant Dixit
  • 5,388
  • 5
  • 17
  • 29
  • ahhhh makes perfect sense, but now the implementation seems fuzzy... It doesn't make sense to make the AJAX call in setQueryParams, but the AJAX call is being made as soon as setQueryParams is done running. Is there a way to stop execution in `componentDidMount()` until the state is set, or is the only way to use the callback to make the AJAX request? – Yitzhak Jun 12 '18 at 05:10
  • Updated check now I used callback function for this solution hope this will solve your problem You can use promise but callback will be best . – Nishant Dixit Jun 12 '18 at 05:23
0

State Updates May Be Asynchronous

0

The setState is an Async function, so it gets time to set your value as state and when you log the state value, the new value hasn't been set yet.

zerob4wl
  • 69
  • 7
0

From the react docs:

Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.

This is why you are not getting the response you expect immediately. However, somewhat confusingly, there are times when set state is immediate. However, you should never expect it to be.

Since the state change is asynchronous, react provides a way to, ahem, react to the state after it has been changed, by means of a callback. So you can write code like this:

this.setState({
  query: params
},(updatedState) => { 
 console.log("updated state:", updatedState)
});
David Schumann
  • 13,380
  • 9
  • 75
  • 96
Garrett Motzner
  • 3,021
  • 1
  • 13
  • 30
  • In other words, since the AJAX call is dependent on the state, I have to make the AJAX call from the callback? – Yitzhak Jun 12 '18 at 05:14
  • Yes, if you need to respond the updated state, then it is best to use a callback, although you could use another lifecycle method such as `componentDidUpdate`, or even a state manager such as mobx or redux. (But the callback should work fine) – Garrett Motzner Jun 12 '18 at 05:27
  • For this particular problem, I ended up just using `this.props.location.query` since that isn't an asynchronous load. However, this will be super helpful when I implement AJAX with filters. I'm storing the filter status in state and building query parameters from it, so I'll run into the same problem. Thanks! – Yitzhak Jun 12 '18 at 05:29