循环调用React状态设置器时数组仅保留最后一项问题排查
问题排查与修复
问题根源
你遇到的核心问题是闭包捕获了旧的state值。在onDrop执行时,forEach循环里的每个reader.onloadstart和onloadend回调都会捕获当前的fileData状态(也就是循环启动时的初始空数组)。当这些异步回调触发时,它们都基于这个旧数组进行更新,导致每次添加新文件都是从空数组重新开始,最终状态里只会保留最后一个文件。
另外还有个明显的笔误:onloadend里你把isLoading设置成了true,这不符合逻辑,加载完成后应该改成false。
修复方案
使用React的函数式更新,给setFileData传入一个函数,这个函数的参数是当前最新的state值,这样每次更新都能基于最新状态修改,不会被闭包的旧值干扰。
修改后的代码:
const [fileData, setFileData] = useState< { name: string; isLoading: boolean; }[] >([]); const onDrop = (acceptedFiles: File[]) => { acceptedFiles.forEach(file => { const reader = new FileReader(); reader.onloadstart = () => { // 函数式更新,基于最新state添加新文件 setFileData(prev => [...prev, { name: file.name, isLoading: true }]); }; reader.onloadend = () => { // 函数式更新,找到对应文件并修改加载状态为完成 setFileData(prev => prev.map(item => item.name === file.name ? { ...item, isLoading: false } : item ) ); }; reader.readAsBinaryString(file); }); };
额外提示
- 函数式更新是处理异步场景下状态更新的标准方案,循环、定时器、异步回调这类场景都适用,能保证状态更新的准确性。
- 如果存在重名文件,上面的代码会同时修改所有同名文件的状态。可以给每个文件生成唯一标识(比如自定义UUID、利用
file.webkitRelativePath)来避免这个问题。
内容的提问来源于stack exchange,提问作者HomeBake
相关产品推荐
相关产品推荐

