Reputation: 2072
I am working on an exercise in the Udemy Advanced Webdeveloper Bootcamp. The exercise asked to come up with a page of 32 boxes that randomly change colour (every x seconds). My solution is not exactly that. I change the color of all 32 boxes at the same time. It almost works. I get random 32 boxes initially, but does not change the color later. My console tells me I am doing something wrong with the setState. But I cannot figure out what. I think my changeColor is a pure function:
import React, { Component } from 'react';
import './App.css';
class Box extends Component {
render() {
var divStyle = {
backgroundColor: this.props.color
}
return(
<div className="box" style={divStyle}></div>
);
}
}
class BoxRow extends Component {
render() {
const numOfBoxesInRow = 8;
const boxes = [];
for(var i=0; i < numOfBoxesInRow; i++) {
boxes.push(<Box color={this.props.colors[i]} key={i+1}/>);
}
return(
<div className="boxesWrapper">
{boxes}
</div>
);
}
}
class BoxTable extends Component {
constructor(props) {
super(props);
this.getRandom = this.getRandom.bind(this);
this.changeColors = this.changeColors.bind(this);
this.state = {
randomColors: this.getRandom(this.props.allColors, 32) // hardcoding
};
this.changeColors();
}
changeColors() {
setInterval(
this.setState({randomColors: this.getRandom(this.props.allColors, 32)}), 5000);
}
getRandom(arr, n) {
var result = new Array(n),
len = arr.length,
taken = new Array(len);
if (n > len)
throw new RangeError("getRandom: more elements taken than available");
while (n--) {
var x = Math.floor(Math.random() * len);
result[n] = arr[x in taken ? taken[x] : x];
taken[x] = --len in taken ? taken[len] : len;
}
return result;
}
render () {
const numOfRows = 4;
const rows = [];
for(let i=0; i < numOfRows; i++) {
rows.push(
<BoxRow colors={this.state.randomColors.slice(8*i,8*(1+i))} key={i+1}/>
)
}
return (
<div className="rowsWrapper">
{rows}
</div>
);
}
}
BoxTable.defaultProps = {
allColors: ["AliceBlue","AntiqueWhite","Aqua","Aquamarine","Azure","Beige",
"Bisque","Black","BlanchedAlmond","Blue","BlueViolet","Brown","BurlyWood",
"CadetBlue","Chartreuse","Chocolate","Coral","CornflowerBlue","Cornsilk",
"Crimson","Cyan","DarkBlue","DarkCyan","DarkGoldenRod","DarkGray","DarkGrey",
"DarkGreen","DarkKhaki","DarkMagenta","DarkOliveGreen","Darkorange",
"DarkOrchid","DarkRed","DarkSalmon","DarkSeaGreen","DarkSlateBlue",
"DarkSlateGray","DarkSlateGrey","DarkTurquoise","DarkViolet","DeepPink",
"DeepSkyBlue","DimGray","DimGrey","DodgerBlue","FireBrick","FloralWhite",
"ForestGreen","Fuchsia","Gainsboro","GhostWhite","Gold","GoldenRod","Gray",
"Grey","Green","GreenYellow","HoneyDew","HotPink","IndianRed","Indigo",
"Ivory","Khaki","Lavender","LavenderBlush","LawnGreen","LemonChiffon",
"LightBlue","LightCoral","LightCyan","LightGoldenRodYellow","LightGray",
"LightGrey","LightGreen","LightPink","LightSalmon","LightSeaGreen",
"LightSkyBlue","LightSlateGray","LightSlateGrey","LightSteelBlue",
"LightYellow","Lime","LimeGreen","Linen","Magenta","Maroon",
"MediumAquaMarine","MediumBlue","MediumOrchid","MediumPurple",
"MediumSeaGreen","MediumSlateBlue","MediumSpringGreen","MediumTurquoise",
"MediumVioletRed","MidnightBlue","MintCream","MistyRose","Moccasin",
"NavajoWhite","Navy","OldLace","Olive","OliveDrab","Orange","OrangeRed",
"Orchid","PaleGoldenRod","PaleGreen","PaleTurquoise","PaleVioletRed",
"PapayaWhip","PeachPuff","Peru","Pink","Plum","PowderBlue","Purple",
"Red","RosyBrown","RoyalBlue","SaddleBrown","Salmon","SandyBrown",
"SeaGreen","SeaShell","Sienna","Silver","SkyBlue","SlateBlue","SlateGray",
"SlateGrey","Snow","SpringGreen","SteelBlue","Tan","Teal","Thistle",
"Tomato","Turquoise","Violet","Wheat","White","WhiteSmoke","Yellow","YellowGreen"]
}
export default BoxTable
Upvotes: 0
Views: 55
Reputation: 2918
You will need to update the state after the component creation phase, inside componentDidMount()
class BoxTable extends Component {
constructor(props) {
super(props);
this.getRandom = this.getRandom.bind(this);
this.changeColors = this.changeColors.bind(this);
this.state = {
randomColors: this.getRandom(this.props.allColors, 32) // hardcoding
};
// delete this line
//this.changeColors();
}
// replace changeColors by componentDidMount,
// this function will be called automatically by react
componentDidMount() {
setInterval(
this.setState({randomColors: this.getRandom(this.props.allColors, 32)}), 5000);
}
getRandom(arr, n) {
var result = new Array(n),
len = arr.length,
taken = new Array(len);
if (n > len)
throw new RangeError("getRandom: more elements taken than available");
while (n--) {
var x = Math.floor(Math.random() * len);
result[n] = arr[x in taken ? taken[x] : x];
taken[x] = --len in taken ? taken[len] : len;
}
return result;
}
render () {
const numOfRows = 4;
const rows = [];
for(let i=0; i < numOfRows; i++) {
rows.push(
<BoxRow colors={this.state.randomColors.slice(8*i,8*(1+i))} key={i+1}/>
)
}
return (
<div className="rowsWrapper">
{rows}
</div>
);
}
}
Upvotes: 1
Reputation: 570
You are calling this.setState
before the component has mounted (from the constructor). Try instead making your first this.ChangeColors
from the componentDidMount
lifecycle function.
Additionally, it's not a bad idea to clear the interval when it unmounts in componentWillUnMount
Edit: By changing the interval to call after the first wait you do prevent the error, for now. I'd recommend using the lifecycle functions to build good habits. It's just a temporary assignment, but in a full project you'd be putting yourself at risk to break the component again later by making it possible to call this.setState
before it is reliably mounted.
Upvotes: 1
Reputation: 625
try to change your changeColors function to like this:
changeColors() {
setInterval(() => this.setState({randomColors: this.getRandom(this.props.allColors, 32)}), 5000);
}
the first param of setInterval is function, in your original code you already executed this setState and didn't passed the function itself
Upvotes: 1
Reputation: 135
You need to use a lambda function in order to use setState inside setInterval
setInterval(() => {
this.setState({randomColors: this.getRandom(this.props.allColors,
32)});
}, 5000)
Upvotes: 1