有些时候,我们想在请求后再去更新组件的状态从而更新UI,比如删除一个列表的某一个项。
整个流程就是用户先点击按钮,按钮发起删除请求api,api发送成功后执行回调,回调里面执行state的更改,从而从新渲染页面。
若获得响应时,组件已经被移除,则会导致互相引用,React 抛出一个错误,告诉你这样会导致内存溢出。
index.js:1 Warning: Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method
我们可以利用全局的事件分发和监听机制避免解决这个问题
父组件
import React, { Component } from "react";
import AfterUnmountSetState from './AfterUnmountSetState';
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
isShow: true
}
}
componentDidMount() {
setTimeout(() => {
this.setState({
isShow: false
})
}, 3000) // 假设3秒后移除组件
}
render() {
return <div>
{this.state.isShow && <AfterUnmountSetState></AfterUnmountSetState>}
</div>;
}
}
子组件
import React, { Component } from "react";
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
data: 1
}
this.eventName = 'AfterUnmountSetState'
this.query = this.query.bind(this)
this.handleChange = this.handleChange.bind(this)
}
query() {
console.log('发送了请求')
// 模拟请求
setTimeout(()=> {
// 发送请求后通知操作数据更新ui,使用全局通知。而不是直接操作ui。
const event = new Event(this.eventName);
// 也可实用传递参数的自定义事件
// const event = new CustomEvent(this.eventName, {'detail': {your_data: '...'})
// e.detail
window.dispatchEvent(event);
}, 2000) // 假设请求阻塞花了2秒。
// 点击多次的时候发送多次请求,也分发出事件给window,让window来代理处理state更新函数。
// 如果你去除这个部分,则当父组件移除子组件时,会出现报错提醒。
}
handleChange() {
this.setState({
data: this.state.data + 1
})
}
componentDidMount() {
console.log('加载完毕监听请求事件')
window.addEventListener(this.eventName, this.handleChange);
}
componentWillUnmount() {
console.log('卸载前取消监听请求事件')
// 移除删除监听
window.removeEventListener(this.eventName, this.handleChange);
}
render() {
return <div>
{this.state.data}
<button onClick={this.query}>请求</button>
</div>;
}
}
这种做法只适合当父组件可能移除子组件并且子组件内部还有状态需要延迟处理的情况。主要是避免更新状态时已经没有该组件导致报错。