React async onClick事件多次setState未按预期工作
异常产生原因
核心是踩了React函数组件的闭包陷阱,同时没有正确使用useState的函数式更新特性:
- 函数组件每次渲染都会生成独立的闭包,绑定在点击事件上的
handleClick是点击触发当下那次渲染的版本,它从执行开始到结束,闭包里捕获的slice值永远是点击那一刻的固定初始值,不会因为中途state更新触发重渲染就发生改变。 - 原实现中
transition函数读取的是闭包里捕获的固定slice值,没有获取状态更新后的实时值:第一次调用transition时,用初始的map.active算出新值map.inactive触发更新,组件重渲染后slice已经变成inactive,但正在执行的旧handleClick里的slice还是最开始的active。 - 等
await stall()结束第二次调用transition时,还是拿旧的active值计算,得到的新值依然是map.inactive,和当前已经更新到inactive的state值完全相同,React判定状态无变化会直接跳过更新,就出现了第二次调用不生效的现象。 - 你自行实现的手动设置版本能正常运行,本质是因为用到了useState的函数式更新能力,拿到了React传递的最新状态值,没有依赖闭包捕获的旧值。
可行修复方案
修改transition的实现,不要依赖闭包中捕获的slice,改用setSlice的函数式更新语法,从回调参数中拿到React保证的最新状态值即可:
const transition = (getMethod) => { setSlice((latestSlice) => { const getNewSlice = getMethod(latestSlice); return getNewSlice(map); }); };
改完之后不管是单次调用、同一事件流里连续多次调用、还是夹杂异步等待的多次调用,都能基于上一次更新后的最新状态计算新值,不会出现更新丢失的问题。
内容的提问来源于stack exchange,提问作者BGS
相关产品推荐
相关产品推荐

