我试图将此代码作为“增量”游戏运行,其中选项将在一些积分后显示,但我得到以下警告(仅一次):
index.js:2178警告:在现有状态期间无法更新
转换(例如在render
或其他组件内
构造函数)。渲染方法应该是道具的纯函数
州;构造函数的副作用是反模式,但可以移动
到componentWillMount
。
import React, { Component } from 'react';
import { Grid, Button } from "semantic-ui-react";
class EventDashboard extends Component {
constructor(props) {
super(props)
this.state={
credits: 0,
newOption: false,
newOptionDirty: false,
}
this.addCredits = this.addCredits.bind(this)
this.renderNewOption = this.renderNewOption.bind(this)
this.intervalID = null;
}
addCredits() {
this.setState((previousState) => ({
credits: previousState.credits + 1
}))
}
componentDidMount() {
this.intervalID = setInterval(() => {
this.addCredits()
}, 1000)
}
componentWillUnmount() {
clearInterval(this.intervalID);
}
renderNewOption() {
if(this.state.credits === 5 && !this.state.newOptionDirty) {
// the warning happens here
this.setState(() =>({
newOptionDirty: true
}))
}
if(this.state.newOptionDirty) {
return(
<div>
<Button> Add new feature </Button>
</div>
)
} else {
return (
<div>no options until 5 credits</div>
)
}
}
render() {
return (
<Grid>
<Grid.Column width={10}>
<Button
onClick={this.addCredits}
>AddCredits</Button>
{this.renderNewOption()}
</Grid.Column>
<Grid.Column width={6}>
<h1>Credits</h1>
<h2>{this.state.credits}</h2>
</Grid.Column>
</Grid>
)
}
}
export default EventDashboard
尽管有这种警告,一切正常。
我错过了什么好习惯?
答案 0 :(得分:1)
每当您在组件中执行任何异步操作(如callbacks / Promises / setTimeout)时,您可能会遇到组件可能已卸载的情况,并且您将尝试更新可能导致的已安装组件(如您的情况)内存泄漏。这是外部状态管理库和中间件(如redux和redux-saga / redux-observable)的用例之一。
如果要在组件中执行此操作,则在卸载组件时需要注意进行必要的清理。这是componentWillUnmount
用于:
constructor(props) {
super(props)
...
this.intervalID = null;
}
componentDidMount() {
this.intervalID = setInterval(() => {
this.addCredits()
}, 1000)
}
componentWillUnmount() {
clearInterval(this.intervalID);
}
答案 1 :(得分:1)
问题确实在于您的renderNewOption()
方法以及如何使用它。
您正在从renderNewOption()
调用render()
,这是不好的,因为您正在执行setState()
。请记住,只要执行setState()
,就会更新状态并触发新的渲染。因此,在setState()
内部render()
将创建一个无限循环,因为渲染函数将继续调用自身。
在这种特殊情况下,你实际上不会得到一个无限循环,因为你有一个if
- 这种情况会阻止它,但是你的组件会在第一次运行时(newOptionDirty
仍在运行时) false
)。发生这种情况时,您的组件尚未安装,因为render()
在调用此特定setState()
之前从未完成。
TL; DR: 在渲染过程中切勿致电setState()
。
请改为尝试:
componentDidMount() {
setInterval(() => {
this.addCredits()
}, 1000);
}
addCredits() {
this.setState((previousState) => ({
credits: previousState.credits + 1
});
}
renderNewOption() {
if(this.state.credits >= 5) {
return(
<div>
<Button> Add new feature </Button>
</div>
)
} else {
return (
<div>no options until 5 credits</div>
)
}
}
我们不需要(也不应该)在渲染中创建一些新的状态变量。我们感兴趣的是credits
是否等于5
。
根据其值,我们返回相应的React元素(按钮或无按钮)。