问题描述
项目中,在网络请求没有返回之前就跳转到另外的页面,结果发现控制台报错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 a useEffect cleanup function.
写法如下:
useEffect(() => {
const getData = async() => {
try {
const resp: any = await fetch('/api/xxx')
setData(resp)
} catch(error) {
console.log(error)
}
}
getData()
}, [])
原因解读
上面的英文报错,告诉我们无法对已卸载的组件执行状态更新,它表示应用程序中存在内存泄漏。
因为在请求还没返回之前,用户进行了页面跳转,跳转之后当前组件被卸载。而在卸载之后,请求服务才返回响应数据,此时执行了await之后的代码(也就是setData),对组件执行状态更新,但此时组件已经被卸载,所以控制台才会抛出报错。
解决方案
方法一
useEffect(() => {
let isUnMount = false
const getData = async() => {
try {
const resp: any = await fetch('/api/xxx')
// 判断当前组件有没有被卸载
if (isUnMount) return
setData(resp)
} catch(error) {
console.log(error)
}
}
getData()
return () => { isUnMount = true } // 通过给useEffect返回一个可执行的回调函数,当组件卸载时,会执行此回调设置isUnMount = true
}, [])
方法二
当请求很多时,这样写存在代码冗余,自然是不合适的,下面看第二种方法(定义一个useEffect在最上层,并且不执行其他操作,只返回回调给useEffect,确保优先执行)
const isUnMount = useRef(false) // 使用useRef来同步当前的卸载状态
useEffect(() => {
return () => { isUnMount.current = true } // 通过给useEffect返回一个可执行的回调函数,当组件卸载时,会执行此回调设置isUnMount = true
}, [])
useEffect(() => {
const getDataAaa = async() => {
try {
const resp: any = await fetch('/api/aaa')
// 判断当前组件有没有被卸载
if (isUnMount.current) return
setDataAaa(resp)
} catch(error) {
console.log(error)
}
}
getDataAaa()
}, [])
useEffect(() => {
const getDataBbb = async() => {
try {
const resp: any = await fetch('/api/bbb')
// 判断当前组件有没有被卸载
if (isUnMount.current) return
setDataBbb(resp)
} catch(error) {
console.log(error)
}
}
getDataBbb()
}, [])