我正在构建一个具有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() {
this.unmounted = true;
}
...
someMethod() {
if (!this.unmounted) this.setState{...}
}
解决此问题的一种方法如下
class X extends Component {
mounted = false
ss = (...args) => {
this.mounted && this.setState(...args)
}
componentDidMount(){
this.mounted = true
}
componentWillUnMount(){ // or componentDidUnmount
this.mounted = false
}
}
现在你可以使用this.ss
而不是this.setState
或者你可以在设置状态之前检查this.mounted
我不确定这是最好的解决方案,但它确实解决了这个问题