【发布时间】:2020-06-17 21:22:35
【问题描述】:
我有一个简单的发现不同颜色的应用程序。游戏的重点只是从集合中选择不同的颜色。在 5 个点之后,该集合呈现为 3x3 而不是 2x2。但是我遇到了这个错误
“未捕获的不变违规:超过最大更新深度。当组件在 componentWillUpdate 或 componentDidUpdate 内重复调用 setState 时,可能会发生这种情况。React 限制嵌套更新的数量以防止无限循环。 在不变量“
我尝试将其上传到 codepen,但由于无限循环,一旦遇到错误,它似乎会终止应用程序。 我阅读了这个问题,他们说 componentDidUpdate 中的 setState 可能会导致另一个更新,然后 inf 循环,但我不确定我是如何导致这个问题的。
componentDidUpdate() {
if (this.state.score === 4) {
this.setState({ size: 9 });
} else if (this.state.score === 9) {
this.setState({ size: 16 });
}
}
class ColorGame extends React.Component {
constructor(props) {
super(props);
this.colorSet = [['blue', '#EA401B'], ['yellow', '#34AD44'], ['green', '#80279D'], ['pink', 'purple']];
this.pickColorPair = this.pickColorPair.bind(this);
this.loadColor = this.loadColor.bind(this);
this.randomize = this.randomize.bind(this);
this.isMatch = this.isMatch.bind(this);
this.increment = this.increment.bind(this);
this.state = {
colors: [],
score: 0,
colorPair: [],
size: 4
}
}
pickColorPair() {
const randomNumber = Math.floor(Math.random() * 4);
this.setState({ colorPair: this.colorSet[randomNumber] }, () => { this.loadColor() });
}
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 }));
}
randomize(colorArray) {
for (let i = colorArray.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1)); // random index from 0 to i
// swap elements array[i] and array[j]
// we use "destructuring assignment" syntax to achieve that
// you'll find more details about that syntax in later chapters
// same can be written as:
// let t = colorArray[i]; colorArray[i] = colorArray[j]; colorArray[j] = t
[colorArray[i], colorArray[j]] = [colorArray[j], colorArray[i]];
}
return (colorArray);
}
isMatch(color) {
let counter = 0;
//We only need to compare the first 3 to know if we got the right answer
for (let i = 0; i < 3; i++) {
if (color === this.state.colors[i]) {
counter++;
}
}
if (counter < 2) {
console.log("CORRECT!");
this.increment();
this.pickColorPair();
this.loadColor();
} else {
console.log("INCORRECT GUESS!");
}
}
increment() {
this.setState((prevState) => ({ score: prevState.score + 1 }));
console.log(this.state.score);
}
// ====== LIFE CYCLE METHODS =====================
//problem comes from this.setState taking a while // i think we can use promises to force it to resolve quicker..
componentDidUpdate() {
if (this.state.score === 4) {
this.setState({ size: 9 });
} else if (this.state.score === 9) {
this.setState({ size: 16 });
}
}
render() {
return (
<div className="container">
<h1>Spot The Difference</h1>
<h2>Score: {this.state.score}</h2>
<h2>Size: {this.state.size}</h2>
<button className='startbtn' onClick={this.pickColorPair}>Start</button>
<GameBoard
colors={this.state.colors}
isMatch={this.isMatch}
score={this.state.score} />
</div>
);
};
}
const GameBoard = (props) => (
<div className="gameboard">
{
props.colors.map((color, index) => (
<ColorCircle
key={index}
color={color}
isMatch={props.isMatch}
score={props.score}
/>
))
}
</div>
)
class ColorCircle extends React.Component {
constructor(props) {
super(props);
this.isMatch = this.isMatch.bind(this);
this.levelMode = this.levelMode.bind(this);
}
levelMode() {
console.log(this.props.score)
if (this.props.score < 5) {
return 'colorCircle-level1';
} else if (this.props.score > 9) {
return 'colorCircle-level3';
}
else if (this.props.score >= 4) {
return 'colorCircle-level2';
}
}
isMatch() {
this.props.isMatch(this.props.color);
}
render() {
return (
<div>
<button
className={this.levelMode()}
onClick={this.isMatch}
style={{ backgroundColor: this.props.color }}></button>
</div >
)
}
}
//we can pass in props to the main app through here. {} is the JSX brackets, not an object literal
ReactDOM.render(<ColorGame />, document.getElementById('app'));
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
button {
width: 50px;
height: 50px;
border-radius: 50%;
outline: none;
border: none;
}
#app {
display: block;
margin: auto;
width: 800px;
text-align: center;
}
.container {
width: 60rem;
height: 70rem;
background-color: #004d66;
margin: auto;
}
.gameboard {
display: flex;
flex-wrap: wrap;
margin: auto;
width: 30rem;
// background: white;
}
.startbtn {
margin: 3rem 0 5rem 0;
width: 8rem;
height: 8rem;
}
.colorCircle-level1 {
width: 15rem;
height: 15rem;
}
.colorCircle-level2 {
width: 10rem;
height: 10rem;
}
.colorCircle-level3 {
width: 7rem;
height: 7rem;
}
//Spacing
$s-size: 1.2rem;
$m-size: 1.6rem;
$l-size: 3.2rem;
$xl-size: 4.8rem;
$desktop-breakpoint: 45rem;
// rem (better support for accessibility)
html {
//makes rem = 10px
font-size: 62.5%;
}
body {
font-family: Helvetica, Arial, san-serif;
//now rem is 16px
font-size: $m-size;
background-color: #203c589a;
color: white;
}
button {
cursor: pointer;
}
button:disabled {
cursor: default;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="~/favicon.ico">
<title>Spot The Difference</title>
</head>
<body>
<div id="app"></div>
<script src="/bundle.js"></script>
</body>
</html>
【问题讨论】:
-
Yours 导致了这个问题,因为
this.setState({ size: N })触发componentDidUpdate并且由于this.state.score没有改变,相同的条件将是true和this.setState({ size: N })被再次调用以重新启动进程。 -
你为什么不把它移到你的
isMatch函数中。如果那里的用户分数达到 4,只需升级大小。 -
不要更新 props-or-state 更新时触发的函数中的状态。尤其是没有考虑到您要使此代码做什么:如果您需要映射值,在
render()中执行此操作,然后不理会状态。如果您需要组件自行循环遍历所有值,请使用知道它在做什么的东西,而不是对更新做出反应。 -
@Titus MinusFour Mike 感谢大家的回答!这对我帮助很大。昨天我被困了一段时间。
标签: javascript reactjs