React中for循环调用setState更新数据失效,DOM未正确渲染更新后内容
问题根因
这是典型的React useState闭包陷阱问题:
- 你定义的
refreshAccount函数内部引用的accounts状态,是函数被创建时所属渲染周期的固定快照 - 批量调用时,所有循环中的
refreshAccount捕获的都是refreshSelected触发时刻的旧accounts值,每次更新都是基于旧值修改,前面的更新结果会被后面的覆盖,所以只有最后1-2个更新生效 - 额外冗余问题:
setIsBusy等React状态更新函数不是异步函数,前面加await无效;setAccounts不需要用setTimeout包裹
修正方案
1. 修改refreshAccount中的状态更新逻辑,改用函数式更新获取最新状态
把原refreshAccount中状态更新部分的代码替换为如下实现:
const refreshAccount = async (id, name, token) => { // 去掉状态更新前的await,此处setIsBusy是同步触发更新 setIsBusy(true) await updateWorkingStatus(id, true, name, token) let work = await axios.get( 'http://192.168.0.101:3000/account/refresh/' + id, ) await updateWorkingStatus(id, false, name, token) setIsBusy(false) // 改用函数式更新,prevAccounts永远是最新的状态值 setAccounts(prevAccounts => { const accs = [...prevAccounts] const index = accs.findIndex(acc => acc._id === id) accs[index] = work.data.result accs[index]._id = id return accs }) return 'OK' }
2. 可选优化:如果不需要逐个等待刷新结果,可以改用并行请求提升速度
如果你的业务允许同时刷新多个账户,可以把refreshSelected改成并行请求实现,不用for循环逐个等待:
const refreshSelected = async () => { const selectedAccounts = accounts.filter(acc => acc.selected) // 不需要加await,filter是同步方法 await Promise.all( selectedAccounts.map(acc => refreshAccount(acc._id, acc.name, acc.token)) ) }
验证逻辑
修改后批量更新时每个setAccounts调用都会基于上一次更新后的最新状态修改,不会出现更新覆盖问题,单独触发刷新的逻辑也不受影响。
内容的提问来源于stack exchange,提问作者JDLR
相关产品推荐
相关产品推荐

