Home > Enterprise >  State not getting updated in React first time event is triggered
State not getting updated in React first time event is triggered

Time:12-19

I am trying to learn react and created a sample app that pulls random dog pictures in from the dog.ceo API.

By default a random image of any dog from any breed is returned. However, there is also a list of breeds that comes from the same API that the user can click to switch the breed they want to see.

So in the BreedList component when someone clicks on one of the breeds the breed they selected is sent back up to the App component using the handleBreedChange() event. This same function then sets the dogBreed state and calls back another function (this.getRandomImage()) to display the actual image using the new dog Breed state.

    handleBreedChange(e) {
        //this.setState({ dogURL: e });
        var CurrentURL = `https://dog.ceo/api/breed/${e}/images/random`;
        this.setState({ dogBreed: e });
        this.setState({ dogURL: CurrentURL });
        console.log(e);
        console.log(this.state);
        this.getRandomImage();
    }

However, I'm running into a weird issue where on the first call the dogBreed state is not updated. It only updates on the second call (the user needs to make 2 actions before the state is current). So when the page is loaded dogBreed is blank. When a breed is clicked it stays blank on the first event call and only updates on the second one.

I read an article on how state updates are async so I imagine that has something to do with it: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

But I'm not sure how to ensure that the state is fully updated before calling the getRandomImage function (assuming that's actually my issue).

Here is my full code on Codesandbox: https://codesandbox.io/p/github/Pawel-IT/thinking_in_react/main?file=/src/App.js

Edit:

Based on what Ray suggested below I passed the expected URL as a argument to the getRandomImage function and if it's set use that instead of the state and that works. Thanks Ray! Is this the best way to do it? Would be nice is there was some function that forced the state to be updated? Or should I not be using the state for this at all?

handleBreedChange(e) {
    //this.setState({ dogURL: e });
    var CurrentURL = `https://dog.ceo/api/breed/${e}/images/random`;
    this.setState({ dogBreed: e });
    this.setState({ dogURL: CurrentURL });
    console.log(e);
    console.log(this.state);
    this.getRandomImage(e);
}

getRandomImage(breed) {
    var breedURL = this.state.dogURL;
    if (breed) {
        breedURL = `https://dog.ceo/api/breed/${breed}/images/random`;
    }

    axios.get(breedURL).then((response) => {
        //setPosts(response.data);
        this.setState({ message: response.data.message });
    });
}

CodePudding user response:

State won’t be updated until the next component render. getRandomImage won’t have the updated state prior to that. If it needs the new value you could pass it as an argument, or as a callback to setState.


Update:

I'm not sure what you mean by "force state to update", but there are numerous ways to handle what you're trying to do, including the suggestions in my answer and elsewhere.

It's not clear that you need state for the breed image at all here. Why not have a <RandomBreedImage breed={this.state.breed} /> component and let that component handle it?

Your handleBreedChange state update will trigger a re-render, and RandomBreedImage will get the new prop.

CodePudding user response:

Why not pass the updated state as a parameter to the getRandomImage function.

   this.getRandomImage({ dogBreed: e,
 dogURL: CurrentURL });

update:

You can't 'force state to update' however you can wait for it to update. If you are looking for this function to be ran every time the state updates you could use a componentDidUpdate method.

Example:

componentDidUpdate(_prevProps, prevState){
    // only runs if the value of the dogBreed state changes.
    if(this.state.dogBreed !== prevState.dogBreed)
         this.getRandomImage()
}

This method would be ran every time the state or the props of the component updates. If your goal is to have the this.getRandomImage() method executed everytime the dogBreed state update I believe this would be the best way of going about it.

Read more about the componentDidUpdate method and the react lifecycle here https://reactjs.org/docs/react-component.html#componentdidupdate

  • Related