如何在React中正确使用setTimeout?解决连续点击按钮仅执行一次状态更新的问题
问题原因分析
这个问题的核心是闭包导致的旧状态引用,再结合React状态更新的特性共同造成的:
- 当你连续点击两次按钮时,
click函数会立刻触发两次setTimeout,这两个setTimeout里的click2函数都会捕获到点击瞬间的numbers状态(也就是初始的{a:10, b:30, c:100, d: ...}对象)。 - 2秒后两个
click2依次执行,但它们都是基于同一个旧的numbers对象来计算新状态,第二次更新会直接覆盖第一次的结果,所以最终看起来只完成了一次状态更新。
简单来说,两次更新都用了同一个“过期”的状态值,没有基于最新的状态进行计算。
修复方法:使用函数式状态更新
React的useState返回的状态更新函数支持传入一个更新函数,而不是直接传入新的状态值。这个更新函数会接收当前最新的状态作为参数,返回新的状态对象。这样每次更新都会基于最新的状态计算,就不会出现覆盖的问题了。
修改后的代码如下:
const Button = () => { const [numbers, setNumbers] = useState({ a: 10, b: 30, c: 100, d: new Date() }); const click = () => { setTimeout(click2, 2000); }; const click2 = () => { // 改用函数式更新,prevNumbers是当前最新的状态 setNumbers(prevNumbers => ({ a: prevNumbers.a + 1, b: prevNumbers.b + 1, c: prevNumbers.c + 1, d: new Date() })); }; return ( <> <button onClick={click}>+</button> <p>{numbers.a}</p> <p>{numbers.b}</p> <p>{numbers.c}</p> <p>{numbers.d.toLocaleString()}</p> </> ); };
这样修改后,不管你连续点击多少次按钮,每个click2都会基于当时最新的状态来更新,最终状态会累积每次点击的增量。
内容的提问来源于stack exchange,提问作者Батрадз Санакоев
相关产品推荐
相关产品推荐

