如何取消React异步任务,解决组件卸载后更新状态的内存泄漏警告
报错触发逻辑
setTimeout返回值为数值类型的定时器ID,并非Promise对象,你添加的await语法没有任何等待执行的效果,两次编写的代码逻辑本质完全一致。- 警告的核心触发原因:拖放文件触发
handleOnDrop后,你定义的定时器会在1秒后执行state更新操作。如果在这1秒的等待窗口内,当前组件被卸载(例如用户切换路由、关闭承载上传组件的弹窗、父组件销毁当前上传组件),定时器回调触发时操作已不存在的组件的state,就会抛出该警告,同时未清理的定时器会造成内存泄漏。
修复方案
方案1:卸载时清理定时器(更推荐)
该方案直接销毁未执行的异步任务,从根源避免问题出现。
函数组件示例代码:
import { useRef, useEffect } from 'react'; // 组件内部代码 // 定义ref存储定时器ID const timerRef = useRef(null); // 组件卸载时清理未执行的定时器 useEffect(() => { return () => { if (timerRef.current) { clearTimeout(timerRef.current); } }; }, []); const handleOnDrop = data => { const infos = data.map(item => item.data); // 执行新定时器前清理之前的定时器,避免多次拖放生成多余任务 if (timerRef.current) { clearTimeout(timerRef.current); } timerRef.current = setTimeout(() => setInfo([...infos]), 1000); };
方案2:更新state前判断组件挂载状态
通过挂载标记判断是否执行state更新:
import { useRef, useEffect } from 'react'; // 组件内部代码 const isMountedRef = useRef(true); useEffect(() => { // 组件卸载时修改挂载标记 return () => { isMountedRef.current = false; }; }, []); const handleOnDrop = data => { const infos = data.map(item => item.data); setTimeout(() => { // 仅组件挂载时更新state if (isMountedRef.current) { setInfo([...infos]); } }, 1000); };
额外优化建议
如果你的需求是拖放后立刻存入state,无需1秒延迟,可以直接删除setTimeout包装,直接执行setInfo([...infos]),从根源避免异步更新问题。
另外你当前CSVReader的config参数使用了逗号表达式,最终仅会生效{header: false}配置,fastMode和chunk配置都会丢失,建议修改为正确的对象写法:
<CSVReader onDrop={handleOnDrop} onError={handleOnError} style={{}} config={{ fastMode: true, chunk: "LocalChunkSize", header: false }} addRemoveButton onRemoveFile={handleOnRemoveFile} >
内容的提问来源于stack exchange,提问作者KYUN
相关产品推荐
相关产品推荐

