【发布时间】:2018-01-02 01:11:59
【问题描述】:
我正在构建一个包含 3 个主要组件的 React/Redux 应用程序 - 我正在尝试循环使用的说明、StoryFeed 和测验 4 轮(3 + 1 次练习)。
我有一个时钟组件(嵌套在 StoryFeed 中 组件),并且它设置为在计时器时移动到测验组件 达到零。但是,它似乎在调用 setState 之后 卸载并给出无限错误
警告:setState(...): 只能更新挂载或挂载 零件。这通常意味着您在未安装的设备上调用了 setState() 零件。这是无操作的。
我不知道如何防止这种情况。下面是Clock 组件的代码:
import React from 'react'
import PropTypes from 'prop-types'
import ReactInterval from 'react-interval'
class Clock extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0,
timed: props.timed,
counting: true
}
this.tick = this.tick.bind(this)
}
reset() {
this.setState({
counting: true,
count: this.state.count + 1
})
}
componentWillUnmount() {
this.setState({ counting: false }, () => this.props.complete())
}
tick() {
const { count, timed, counting } = this.state
if (count + 1 > timed && counting) {
this.componentWillUnmount()
} else {
this.reset();
}
}
render() {
return (
<div className="clock alert alert-warning">
<ReactInterval
timeout={1000}
enabled={this.props.timed > 1 && this.state.count < this.props.timed}
callback={() => this.tick()}
/>
<span>{this.state.timed - this.state.count}</span>
</div>
)
}
}
Clock.propTypes = {
timed: PropTypes.number,
complete: PropTypes.func
}
export default Clock
这是父组件StoryFeed 代码:
import React from 'react'
import Marquee from './Marquee'
import * as stories from '../stories'
import Clock from './Clock'
import { chunk, now } from '../utils'
import PropTypes from 'prop-types'
class StoryFeed extends React.Component {
constructor(props) {
super(props)
this.state = {
text: stories.example,
currentTest: 1,
count: 0,
timed: props.timed,
selected: []
}
this.storyLoad.bind(this)
this.select = this.select.bind(this)
this.isSelected = this.isSelected.bind(this)
}
componentDidMount() {
document.body.classList.add('mosaic-full-screen')
this.storyLoad();
}
componentWillUnmount() {
document.body.classList.remove('mosaic-full-screen')
}
select(id) {
if (this.state.selected.find(s => s.id == id)) return
this.setState({
selected: [...this.state.selected, { id, time: now() }]
})
}
isSelected(id) {
return this.state.selected.find(j => j.id === id)
}
storyLoad(state) {
switch (this.state.currentTest){
case 1:
this.setState({text: stories.example});
console.log(this.state.currentTest)
break;
case 2:
this.setState({text: stories.colleagues});
break;
case 3:
this.setState({text: stories.aroomforthenight});
break;
case 4:
this.setState({text: stories.thepromotion});
break;
}
};
reset() {
this.clock &&
this.clock.reset(4, () => {
this.setState({
counting: true
})
})
}
render() {
const { enterAnswers, id, show, timed } = this.props
return (
<div className="story">
<div className='container'>
<Marquee text={this.state.text.join(' - ')} loop={false} hoverToStop={true} />
</div>
<div className="controls">
{timed && (
<Clock
timed={timed}
complete={() => enterAnswers(id, this.state.selected, now())}
/>
)}
</div>
</div>
)
}
}
StoryFeed.propTypes = {
timed: PropTypes.number,
enterAnswers: PropTypes.func,
id: PropTypes.number,
show: PropTypes.oneOf(['window', 'jigsaw'])
}
export default StoryFeed
这个问题的其他答案似乎是针对具体情况的
【问题讨论】:
-
我发现你自己打电话给
componentWillUnmount很有趣,但是这是一个反应生命周期事件,应该在卸载组件之前由反应调用。你知道哪个组件发出警告吗?我想知道它是渲染StoryFeed组件的组件还是真的是Clock组件 -
@Icepickle 对
componentWillUnmount的调用背后的原因似乎是关闭此组件时正在修改主体本身(在 React 根节点之外)。完全合法地使用这种生命周期方法! -
@ZekeDroid 当然,你可以调用它,但是,我认为不应该调用它,为什么不提供一个额外的方法,既可以从 tick 方法也可以从 @ZekeDroid 调用987654329@方法?您也不会在自己的答案中这样做,您只需设置一个标志,将其标记为未安装。所以是的,你可以这样称呼它,它是否提高了可读性或可维护性,不,我不这么认为,因为它总是会提出同样的问题
-
哦,伙计,我对你所说的很不满意,因为我没有阅读整个代码 sn-p!不,你是完全正确的,它应该是它自己的方法。用户代码不应直接调用生命周期方法。