ComponentDidMount 代码即使在组件卸载后也执行
ComponentDidMount Code Executing even after component unmounts
在 ComponentDidMount() 内部,我们定期调用 api 来获取数据。但是 api 即使在组件卸载后仍会被调用。
async componentDidMount() {
let { getItemHistoryData } = this.props;
for (let i = 0; i < 25 && !this.props.is_synched; i++) {
await wait(5000);
getItemHistoryData();
}
}
const wait = (ms) => {
return new Promise(resolve => setTimeout(resolve, ms));
}
但即使用户移动到不同的页面,getItemHistoryData() 操作也会被调用。
我认为您需要重构一下您的代码...
您需要跟踪您的 setTimeout
才能在 componentWillUnmount()
上停止它
let t;
async componentDidMount() {
let { getItemHistoryData } = this.props;
t = setTimeout(getItemHistoryData(), 5000);
}
componentWillUnmount() {
clearTimeout(t);
t = null;
}
getItemHistoryData() {
// Do the function's stuff here
if(!this.props.is_synched){
t = setTimeout(getItemHistoryData(), 5000);
}
}
在 getItemHistoryData()
的末尾做关于 i < 25 && !this.props.is_synched
的事情,然后再次调用或不调用 setTimeout
componentDidMount 运行并完成。但它有一个承诺,当实现时将调用承诺内的回调。这是由 javascript 引擎完成的。与react无关。
编辑:看起来 for 循环确实已完全执行,但在 await
处阻塞。所以它创建了一个承诺链,而不是一次创建 25 个承诺。需要做更多的研究。我虽然只有 for-of 循环会那样做。对于错误信息,我们深表歉意。
这就是您的代码转换为 es5 后的样子Converted code with Babel JS
我给你更多我喜欢使用的选项
保存状态:
intervalId: null,
intervalCounter: 0
和 componentDidMount 添加:
const intervalId = setInterval(this.timer, 5000, 25)
创建一个新函数timer
:
timer = async () => {
if(this.state.intervalCounter <= 25){
getItemHistoryData();
this.setState({intervalCounter: this.state.intervalCounter + 1})
} else {
clearInterval(this.state.intervalId); //clear
}
}
并且不要忘记在用户离开屏幕时清除间隔:
componentWillUnmount() {
clearInterval(this.state.intervalId);
}
我从上面的代码中了解到,即使组件已卸载,您的循环也会 运行 直到完成。要在不对代码进行重大更改的情况下解决此特殊情况,您可以使用布尔值来跟踪组件卸载并在循环条件中使用相同的布尔值以确保循环在卸载后的迭代中退出。请注意,在您的组件卸载后,这仍然会执行一次挂起的任务。
async componentDidMount() {
let { getItemHistoryData } = this.props;
for (let i = 0; i < 25 && !this.props.is_synched && !this.isUnmounted; i++) {
await wait(5000);
getItemHistoryData();
}
}
async componentWillUnmount(){
this.isUnmounted = true;
}
如果您想阻止任何进一步的迭代,您可以重构您的代码以使用 setInterval,然后使用 clearInterval 进行清理。对于大多数实际情况,我上面建议的方法应该足够了,并且可以防止重构代码。
在 ComponentDidMount() 内部,我们定期调用 api 来获取数据。但是 api 即使在组件卸载后仍会被调用。
async componentDidMount() {
let { getItemHistoryData } = this.props;
for (let i = 0; i < 25 && !this.props.is_synched; i++) {
await wait(5000);
getItemHistoryData();
}
}
const wait = (ms) => {
return new Promise(resolve => setTimeout(resolve, ms));
}
但即使用户移动到不同的页面,getItemHistoryData() 操作也会被调用。
我认为您需要重构一下您的代码...
您需要跟踪您的 setTimeout
才能在 componentWillUnmount()
let t;
async componentDidMount() {
let { getItemHistoryData } = this.props;
t = setTimeout(getItemHistoryData(), 5000);
}
componentWillUnmount() {
clearTimeout(t);
t = null;
}
getItemHistoryData() {
// Do the function's stuff here
if(!this.props.is_synched){
t = setTimeout(getItemHistoryData(), 5000);
}
}
在 getItemHistoryData()
的末尾做关于 i < 25 && !this.props.is_synched
的事情,然后再次调用或不调用 setTimeout
componentDidMount 运行并完成。但它有一个承诺,当实现时将调用承诺内的回调。这是由 javascript 引擎完成的。与react无关。
编辑:看起来 for 循环确实已完全执行,但在 await
处阻塞。所以它创建了一个承诺链,而不是一次创建 25 个承诺。需要做更多的研究。我虽然只有 for-of 循环会那样做。对于错误信息,我们深表歉意。
这就是您的代码转换为 es5 后的样子Converted code with Babel JS
我给你更多我喜欢使用的选项
保存状态:
intervalId: null,
intervalCounter: 0
和 componentDidMount 添加:
const intervalId = setInterval(this.timer, 5000, 25)
创建一个新函数timer
:
timer = async () => {
if(this.state.intervalCounter <= 25){
getItemHistoryData();
this.setState({intervalCounter: this.state.intervalCounter + 1})
} else {
clearInterval(this.state.intervalId); //clear
}
}
并且不要忘记在用户离开屏幕时清除间隔:
componentWillUnmount() {
clearInterval(this.state.intervalId);
}
我从上面的代码中了解到,即使组件已卸载,您的循环也会 运行 直到完成。要在不对代码进行重大更改的情况下解决此特殊情况,您可以使用布尔值来跟踪组件卸载并在循环条件中使用相同的布尔值以确保循环在卸载后的迭代中退出。请注意,在您的组件卸载后,这仍然会执行一次挂起的任务。
async componentDidMount() {
let { getItemHistoryData } = this.props;
for (let i = 0; i < 25 && !this.props.is_synched && !this.isUnmounted; i++) {
await wait(5000);
getItemHistoryData();
}
}
async componentWillUnmount(){
this.isUnmounted = true;
}
如果您想阻止任何进一步的迭代,您可以重构您的代码以使用 setInterval,然后使用 clearInterval 进行清理。对于大多数实际情况,我上面建议的方法应该足够了,并且可以防止重构代码。