1

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!

1
  • if you need stuff to happen in an order then you should look into promises Commented Jun 16, 2020 at 22:25

2 Answers 2

2

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

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

Comments

2

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

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.