正确卸载 React 组件
Properly unmount React component
问题:为什么在组件不再由其父级渲染后抛出此警告?我是否遗漏了卸载此组件需要做的事情,而不是仅仅过滤作为道具向下传递到组件层次结构的商店状态?
我见过很多这种情况,但解决方案通常是从组件中取消订阅 redux 存储;但是,此组件未连接到商店,仅连接到顶级容器。
remove
操作只是过滤存储状态以删除负责当前组件的数组元素。
refresh
动作目前只是对子组件中 UI 动画事件的模拟。
- 仅当我在调用
refresh
操作后删除 Feed
组件时才抛出警告
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the Feed component.
// @flow
// Feed.js
import React, { Component } from 'react'
import type { FeedType, FeedState } from '../../utils/types'
import { remove, refresh } from '../../actions/redux-actions'
import RssEventList from '../containers/RssEventList'
const cardColors: Array<string> = ['red', 'orange', 'olive', 'green', 'blue', 'yellow']
export default class Feed extends Component {
props: FeedType
state: FeedState
constructor(props: *) {
super(props)
this.state = {
reloading: false
}
}
refresh() {
this.setState({ reloading: true })
setInterval(() => this.setState({ reloading: false }), 4000)
this.props.dispatch(refresh(this.props.link))
}
remove() {
this.props.dispatch(remove(this.props.link))
}
render() {
const color: string = cardColors[Math.floor(Math.random() * cardColors.length)]
return (
<div className={`ui ${color} card`}>
<div className="content">
<div className="ui header">
{this.props.title}
<a className="source link" href={this.props.link} target="_blank">
<i className="linkify right floated icon"></i>
</a>
</div>
<div className="meta">
{this.props.description}
</div>
</div>
<div className="content">
<RssEventList reloading={this.state.reloading} events={this.props.feed} />
</div>
<div className="extra content">
<span className="left floated" onClick={() => this.refresh()}>
<i className="refresh icon"></i>
Refresh
</span>
<span className="right floated" onClick={() => this.remove()}>
<i className="cancel icon"></i>
Remove
</span>
</div>
</div>
)
}
}
如果有帮助,请看下面的组件层次结构图:
App (connected to store)
|- Header
|- FilterBar
|- FeedList
|- Feed
|- RssEventList
|- RssEvent
|- AddCard
问题是您没有将间隔存储在组件上以在组件卸载时将其删除。因此,即使在卸载组件后,该间隔也会继续调用。您需要使用 clearInterval()
:
删除它
export default class Feed extends Component {
refresh() {
this.myInterval = setInterval(() => this.setState({ reloading: false }), 4000)
}
componentWillUnmount() {
clearInterval(this.myInterval);
}
}
问题:为什么在组件不再由其父级渲染后抛出此警告?我是否遗漏了卸载此组件需要做的事情,而不是仅仅过滤作为道具向下传递到组件层次结构的商店状态?
我见过很多这种情况,但解决方案通常是从组件中取消订阅 redux 存储;但是,此组件未连接到商店,仅连接到顶级容器。
remove
操作只是过滤存储状态以删除负责当前组件的数组元素。refresh
动作目前只是对子组件中 UI 动画事件的模拟。- 仅当我在调用
refresh
操作后删除Feed
组件时才抛出警告
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the Feed component.
// @flow
// Feed.js
import React, { Component } from 'react'
import type { FeedType, FeedState } from '../../utils/types'
import { remove, refresh } from '../../actions/redux-actions'
import RssEventList from '../containers/RssEventList'
const cardColors: Array<string> = ['red', 'orange', 'olive', 'green', 'blue', 'yellow']
export default class Feed extends Component {
props: FeedType
state: FeedState
constructor(props: *) {
super(props)
this.state = {
reloading: false
}
}
refresh() {
this.setState({ reloading: true })
setInterval(() => this.setState({ reloading: false }), 4000)
this.props.dispatch(refresh(this.props.link))
}
remove() {
this.props.dispatch(remove(this.props.link))
}
render() {
const color: string = cardColors[Math.floor(Math.random() * cardColors.length)]
return (
<div className={`ui ${color} card`}>
<div className="content">
<div className="ui header">
{this.props.title}
<a className="source link" href={this.props.link} target="_blank">
<i className="linkify right floated icon"></i>
</a>
</div>
<div className="meta">
{this.props.description}
</div>
</div>
<div className="content">
<RssEventList reloading={this.state.reloading} events={this.props.feed} />
</div>
<div className="extra content">
<span className="left floated" onClick={() => this.refresh()}>
<i className="refresh icon"></i>
Refresh
</span>
<span className="right floated" onClick={() => this.remove()}>
<i className="cancel icon"></i>
Remove
</span>
</div>
</div>
)
}
}
如果有帮助,请看下面的组件层次结构图:
App (connected to store)
|- Header
|- FilterBar
|- FeedList
|- Feed
|- RssEventList
|- RssEvent
|- AddCard
问题是您没有将间隔存储在组件上以在组件卸载时将其删除。因此,即使在卸载组件后,该间隔也会继续调用。您需要使用 clearInterval()
:
export default class Feed extends Component {
refresh() {
this.myInterval = setInterval(() => this.setState({ reloading: false }), 4000)
}
componentWillUnmount() {
clearInterval(this.myInterval);
}
}