0

I'm attempting to crack this particular nut where my state isn't updating. I've got a function calling the NASA api through Axios which is working fine. Currently the datePicked state is empty by default which means the GET request defaults to the current date. I'm to adding functionality to generate a random date (function onRandomDateClick), which is all working fine.

Then I want to update the state of datePicked with the generated date, and then run the onImageGet function which holds the axios request.

Initial State

  class Photo extends Component {
    state = {
      apiUrl: 'XXXXXXXX',
      apiKey: 'XXXXXXXX',
      image: null,
      imgChecked: false,
      datePicked: ""
    };

Generates random Date, logs variable and state. State currently displays blank. this.setState isn't working.

    onRandomDateClick = (e) => {
      function randomDate(start, end) {
        return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));
      }
      let randomDateResult = randomDate(new Date(2012, 0, 1), new Date())
      let datePicked = randomDateResult.toISOString().slice(0,10);
      this.setState({datePicked})
      console.log("variable is " + datePicked);
      console.log("state is " + this.state.datePicked);
      this.onImageGet();
    }

Axios call with state here

    onImageGet = e => {
      axios.get(`${this.state.apiUrl}?api_key=${this.state.apiKey}&date=${this.state.datePicked}`)
      //response always starts res.data
          .then(res => {
            const image = res.data;
            this.setState({ image })
          })
          this.setState({imgChecked: true})
    }


    render() {
      ...blah blah...
      return (
        <div className="photo">        

         ... blah blah blah ...
        </div>

      );
    }
  }
9
  • You haven't actually set the state. this.setState({parameter: value}) would be the correct syntax. Commented Jan 29, 2019 at 10:27
  • Another approach is to use redux and and redux-axios-middleware for async operation. Commented Jan 29, 2019 at 10:29
  • js is async. you should do something like this. gist.github.com/nazrdogan/1167c9683a66f038516f527184b024c3 Commented Jan 29, 2019 at 10:31
  • @KenoClayton I was under the impression that if the var name and the state were the same it's possible to do it like this? Is that incorrect? Commented Jan 29, 2019 at 10:33
  • I'm actually not sure, I've never seen that implementation, so I could be mistaken. Commented Jan 29, 2019 at 10:34

2 Answers 2

1

Js is working async for http call. for React setState is async.you should do something like this.

onRandomDateClick = (e) => {
      function randomDate(start, end) {
        return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));
      }
      let randomDateResult = randomDate(new Date(2012, 0, 1), new Date())
      let datePicked = randomDateResult.toISOString().slice(0,10);
      this.setState({datePicked},() => {
        this.onImageGet();
      })
    }
    
    onImageGet = e => {
      axios.get(`${this.state.apiUrl}?api_key=${this.state.apiKey}&date=${this.state.datePicked}`)
      //response always starts res.data
          .then(res => {
            const image = res.data;
            this.setState({ image, imgChecked: true })

          })
        
    }
    render() {
      ...blah blah...
      return (
        <div className="photo">        

         ... blah blah blah ...
        </div>

      );
    }
  }

Sign up to request clarification or add additional context in comments.

2 Comments

It is not JS that is async. It is that the setState is async.
I mean for http call. maybe its misleading. I will edit :)
1

The problem is that setState is asynchronous so when you call onImageGet right after setting the state, the state has not yet been updated and you access the previous one.

You could use the callback method that setState supports which is called once the state has been updated.

this.setState({datePicked}, this.onImageGet)

or a more common pattern would be to check for relevant changes in the componentDidUpdate and call the this.onImageGet there.

  class Photo extends Component {
    state = {
      apiUrl: 'XXXXXXXX',
      apiKey: 'XXXXXXXX',
      image: null,
      imgChecked: false,
      datePicked: ""
    };


    onRandomDateClick = (e) => {
      function randomDate(start, end) {
        return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));
      }
      let randomDateResult = randomDate(new Date(2012, 0, 1), new Date())
      let datePicked = randomDateResult.toISOString().slice(0,10);
      this.setState({datePicked})
      console.log("variable is " + datePicked);
      console.log("state is " + this.state.datePicked);
    }

    onImageGet = e => {
      axios.get(`${this.state.apiUrl}?api_key=${this.state.apiKey}&date=${this.state.datePicked}`)
      //response always starts res.data
          .then(res => {
            const image = res.data;
            this.setState({ image })
          })
          this.setState({imgChecked: true})
    }

    componentDidUpdate(prevProps, prevState){
      if (prevState.datePicked !== this.state.datePicked){
        this.onImageGet();
      }
    }

    render() {
      ...blah blah...
      return (
        <div className="photo">        
         ... blah blah blah ...
        </div>

      );
    }
  }

1 Comment

Thanks man, it's really useful to see different implementations of the solution.

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.