为什么React中useState的setter必须传入函数形式的更新值?
直接写
setFiles([...files, fileState])失效的根本原因 这个问题和react-dropzone本身的逻辑没有关系,是React状态更新的闭包旧值捕获、连续状态更新的覆盖规则在当前场景下的叠加效果,拆解下来其实很明确:
- 直接传值的写法里引用的
files变量,是当前执行上下文被闭包锁死的固定快照值,不是触发更新时React内部保存的最新状态 - 你的上传逻辑跑在
acceptedFiles.forEach的遍历循环里,会在同步执行阶段连续触发多轮setFiles调用,全程等不到React完成一轮状态更新、重新渲染组件生成新的files变量 - 当所有连续触发的更新都是基于同一个旧快照
files值生成时,React处理更新队列的逻辑会让后发的更新直接覆盖先发的结果,最终状态会丢失大部分待上传文件,你看到的“功能失效”就是这么来的。
为什么换成函数式更新就正常了
你原本使用的setFiles(array => [...array, fileState])这种函数式更新写法,等于把状态更新的取值逻辑交给React内部托管:
- React会按更新的排队顺序,把上一次更新完成后的最新状态作为参数传给你写的更新函数,完全不依赖外部闭包捕获的旧值
- 哪怕同一轮同步代码里连续触发十几次状态更新,每一次拿到的
array都是上一次迭代后的结果,最终生成的列表会包含所有待上传文件,不会出现覆盖丢失的问题。
实际失效流程还原:假设一次拖拽上传3个文件
- 组件初次渲染时,闭包内捕获的
files值是初始空数组[]- forEach依次处理3个文件,每一次循环里的
setFiles([...files, fileState])都是在await fetch之前同步执行的,本质都是基于最开始的空数组生成三个独立的更新对象:[文件1]、[文件2]、[文件3]- 整个同步执行过程中,根本等不到React完成一轮状态更新把新的
files值暴露给外部闭包,当React按顺序处理这三个更新时,后一个更新会直接替换前一个的结果,最终files里只会剩下最后一个文件的记录,前面两个文件的状态会被完全覆盖。
内容的提问来源于stack exchange,提问作者Alexander Craggs
相关产品推荐
相关产品推荐

