React中如何正确取消未完成的Ajax请求?
我在用useEffect做数据请求,切换页面时老报“Can’t perform a React state update on an unmounted component”错误。试过AbortController但好像没生效,是不是哪里写错了?
useEffect(() => {
const controller = new AbortController();
fetch('/api/data', { signal: controller.signal })
.then(res => res.json())
.then(data => setData(data));
return () => controller.abort();
}, []);
第一,你的代码没有处理abort后抛出的AbortError。虽然这不会直接导致你看到的那个警告,但console里应该会报unhandled promise rejection。
第二,更关键的是:即使调用了abort(),请求发出去后响应回来时组件可能已经unmount了,这时候setData照常执行,照样触发那个警告。abort()只是告诉浏览器取消请求,但它不能阻止响应回来后执行的then回调。
正确的写法是这样的:
核心改动就两点:
一是用isMounted这个flag在setState前判断组件是否还挂着。二是加了catch捕获AbortError,不然abort时会抛出个未处理的reject。
如果你用的是axios而不是fetch,那情况又不一样,axios自带cancel token,不过这是后话了。你先试试这个能不能解决你的问题。
fetch 被 abort 时会抛出一个 AbortError,这个错误会打断 .then 链,但如果你不 catch 它,默认行为有点坑。而且更重要的,即使请求被取消了,如果此时组件已经卸载,setData 还是会触发那个 unmounted 错误。
正确写法是这样的:
两个要点:
一是加个 isMounted 标记,在 setData 前判断组件是否还挂着。controller.abort() 只能阻止请求发出去或者拿到响应,但已经拿到响应后还是会往下走 then,所以必须自己判断。
二是 catch 里要区分 AbortError 和其他错误。AbortError 是正常的取消行为,不需要抛出来,其他错误(比如网络断了一般还是得处理一下)。
复制过去试试,基本就解决了。