Reputation: 1681
folks. I'm having a probably noob question, but I'll shoot nonetheless. The code snippets below are fairly straightforward, and I shouldn't be having issues but here we go. I'm trying to get the list of colors in the ColorsGrid
component to. In short, when a user changes the difficulty level via the dropdown list, a new set of colors ought to be generated and, thus, displayed. I thought this was a pretty straightforward exercise, but things are not working as expected. Whenever I change the difficulty, it would not react (rerender ColorsGrid
component) and only after I once again select another (difficulty) level would the previous one provoke a rerender. For instance, if I were to select Medium after the initial render (default level is set to Easy), nothing changes. However, if I go back to Easy (or select any other difficulty), then does the changes corresponding to the previous (Medium difficulty) occur i.e. ColorsGrid rerenders and, thus, displays a grid corresponding to medium difficulty. What am I doing wrong?
Below is the relevant code.
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
// Get random rgb color
function randomColor() {
let r = Math.floor(Math.random() * 256);
let g = Math.floor(Math.random() * 256);
let b = Math.floor(Math.random() * 256);
return `rgb(${r}, ${g}, ${b})`;
}
// Set length of color list array as a funciton of difficulty
function colorsListLength(difficulty) {
switch (true) {
case difficulty === 'expert':
return 25;
case difficulty === 'hard':
return 20;
case difficulty === 'medium':
return 10;
default:
return 5;
}
}
// Get color list array
function colorsList(colorsLength = 5) {
const colors = [];
while (colors.length < colorsLength) {
colors.push(randomColor());
}
return colors;
}
// Set random color to guess from (above) color list array
function randomColorToGuess(colors) {
const index = Math.floor(Math.random() * colors.length);
return colors[index];
}
// Set number of game tries as a function of difficulty
function numberOfTries(difficulty) {
switch (true) {
case difficulty === 'expert' || difficulty == 'hard':
return 2;
case difficulty === 'medium':
return 1;
default:
return 0;
}
}
// Colors grid component
function ColorsGrid({ difficulty, colorsList }) {
return (
<div>
<strong>Colors Grid</strong>
<p>Difficulty: {difficulty}</p>
<div>
{colorsList.length > 0 ? (
colorsList.map(color => (
<div
style={{
backgroundColor: color,
height: '3rem',
width: '3rem',
borderRadius: '50%',
}}
key={color}
/>
))
) : (
<div>Loading colors...</div>
)}
</div>
</div>
);
}
// Main component
class App extends Component {
constructor(props) {
super(props);
this.state = {
difficulty: 'easy',
colorsList: [],
};
this.colorsArray = this.colorsArray.bind(this);
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this.colorsArray(this.state.difficulty);
}
colorsArray() {
const colors = colorsList(colorsListLength(this.state.difficulty));
const colorToGuess = randomColorToGuess(colors);
this.setState(() => ({
colorsList: colors,
gameTries: numberOfTries(this.state.difficulty),
colorToGuess,
}));
}
handleChange(e) {
this.setState({
difficulty: e.target.value,
});
this.colorsArray(this.state.difficulty); // I was under the impression the (difficulty) state had already been updated here
}
render() {
return (
<div className="App">
<h1>Colors</h1>
<div style={{ textAlign: 'right' }}>
<select
id="difficulty"
value={this.state.difficulty}
onChange={this.handleChange}
>
<option value="easy">Easy</option>
<option value="medium">Medium</option>
<option value="hard">Hard</option>
<option value="expert">Expert</option>
</select>
</div>
<ColorsGrid
colorsList={this.state.colorsList}
difficulty={this.state.difficulty}
/>
</div>
);
}
}
const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);
Upvotes: 1
Views: 475
Reputation: 2321
The issue is with the coordination of your calls to setState. The following cleas things up:
colorsArray(difficulty) {
const colors = colorsList(colorsListLength(difficulty));
const colorToGuess = randomColorToGuess(colors);
this.setState(() => ({
difficulty,
colorsList: colors,
gameTries: numberOfTries(this.state.difficulty),
colorToGuess
}));
}
handleChange(e) {
this.colorsArray(e.target.value);
}
You can see that your event handler makes one call to the color updating function. This then works out the new colours and sets the state in one place.
Upvotes: -1
Reputation: 15841
This is because setState()
is async:
setState(newState, callback);
In order to get the just selected difficulty you have to change the code like this:
this.setState({
difficulty: e.target.value,
}, () => this.colorsArray(this.state.difficulty)
);
Upvotes: 3