Brian Bui
Brian Bui

Reputation: 123

Running React method after another

I have a really simple React App, where you just click on the different color from the set and it re-renders to another color set. To load the game you press the button Pick color > Loads Color. I tried to create a Start Button that is just a method that calls Pick Color and Loads Color but I dont understand why it is not working properly. It seems like Loads Color runs before Pick Color finishes and that's why there is an error. If I do a setTimeout inside start for Loads Color, it will end up running correctly.

https://codepen.io/dnangels/pen/yLeVBZg

pickColorPair() {
        const randomNumber = Math.floor(Math.random() * 4);
        this.setState(() => ({ colorPair: this.colorSet[randomNumber] }));
        console.log(this.state.colorPair);
    }


    loadColor() {
        // console.log(this.state.colorPair);
        let colorArray = [this.state.colorPair[0]];

        for (let i = 1; i < this.state.size; i++) {
            colorArray.push(this.state.colorPair[1]);
        }
        this.randomize(colorArray);
        this.setState(() => ({ colors: colorArray }));
    }

Does anyone have a solution for this? How can I get Loads Color to run after Pick Color. Thanks!

Upvotes: 1

Views: 73

Answers (2)

Dylan Kerler
Dylan Kerler

Reputation: 2187

This is because setState works asynchronously.

To get this to work the way you intend you can use promises or callbacks - the convention nowadays is to use promises; However, setState uses callbacks because react was created before promises were the de-facto standard. Because of this, you'll need to integrate promises and callbacks together:

pickColorPair() {
    return new Promise(resolve => {
        const randomNumber = Math.floor(Math.random() * 4);
        this.setState(() => ({ 
            colorPair: this.colorSet[randomNumber] 
        }), resolve);
    });
}


loadColor() {
    return new Promise(resolve => {
        /** -- snip -- **/
        this.setState(() => ({ colors: colorArray }), resolve);
    });
}

setState takes a callback function that will be executed after the state has been updated. So after we update the state then we can resolve the promise by passing in resolve function as the callback.

Now our functions have promises we can use them synchronously. Your start function can now use the .then method to load the colours and then pick a colour pair only after the colours have been loaded:

start() {
  this.loadColors().then(this.pickColorPair);
}

You can read more about promises here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Upvotes: 2

Michael Peng
Michael Peng

Reputation: 926

setState is asynchronous, if you need a function to execute right after setState(), you have to put it in the setState() callback, in your example, this might work.

pickColorPair() {
        const randomNumber = Math.floor(Math.random() * 4);
        this.setState(() => ({ colorPair: this.colorSet[randomNumber] }, this.loadColor));
        console.log(this.state.colorPair);
    }

Upvotes: 2

Related Questions