React多文件上传时同一state并发更新的正确处理方式?
React多文件并发上传中State并发更新的解决方案
问题背景
我正在开发一个支持多文件并发上传的React文件上传门户,结合既有代码与自研代码实现。顶层组件通过useState维护名为files的状态变量,该变量为存储各上传文件信息的对象,会被映射生成对应UI元素。单文件上传时状态更新正常,但添加第二个文件后,同一files state被多位置并发更新导致UI渲染异常。
问题根源
当前代码中更新files状态时,直接依赖了当前渲染周期的files变量(比如...files)。在并发更新场景下,多个更新操作会基于同一个旧的state快照执行,导致后执行的更新覆盖先执行的,最终丢失部分状态变更。
正确处理方式:使用函数式更新
React的useState提供了函数式更新的方式,即给状态更新函数传入一个回调函数,该回调会接收上一次的最新state作为参数,返回新的state。这样每次更新都会基于最新的状态值计算新状态,彻底避免并发更新时的状态覆盖问题。
修改后的核心代码
1. 修复updateProgress函数
const updateProgress = (uploadId, progress) => { // 使用函数式更新,基于最新的prevFiles计算新状态 setFiles(prevFiles => ({ ...prevFiles, [uploadId]: { ...prevFiles[uploadId], progress } })); };
2. 修复addNewFile函数
const addNewFile = () => { setFiles(prevFiles => { const filesArray = Object.values(prevFiles); const lastUpload = filesArray[filesArray.length - 1]; const newId = lastUpload.id + 1; return { ...prevFiles, [newId]: { id: newId, name: 'File ' + newId, progress: 0 } }; }); };
3. 修复UploadStatus组件的定时器逻辑
原代码中calls === 10的判断不会生效(因为setInterval是异步执行的),同时需要补全useEffect的依赖项,避免闭包捕获旧值:
const UploadStatus = ({ file, updateProgress, uploading, setUploading }) => { useEffect(() => { if (!uploading) return; let calls = 0; const interval = setInterval(() => { calls++; updateProgress(file.id, calls * 10); // 移到定时器回调内判断,达到100%时停止上传 if (calls === 10) { clearInterval(interval); setUploading(false); } }, 1000); return () => clearInterval(interval); }, [uploading, file.id, updateProgress]); // 补全依赖项 return ( <div key={file.id}> <p>{file.name}</p> <progress value={file.progress} max="100" /> </div> ); }
总结
函数式更新是React处理并发状态更新的标准方案,它确保每次状态更新都基于最新的state快照,不会因为闭包捕获旧state而导致更新丢失。在多个组件或异步操作同时修改同一state时,必须使用这种方式来保证状态的一致性。
内容的提问来源于stack exchange,提问作者user16248109
相关产品推荐
相关产品推荐

