React如何处理按钮短间隔快速双击的事件执行?
关于React多次点击事件的状态更新疑问
React官方文档明确说明:
React不会对点击这类主动触发的多个事件进行批量处理——每次点击都会单独处理。请放心,React仅在通常安全的情况下才会进行批量处理。例如,这确保如果第一次点击按钮禁用了表单,第二次点击不会再次提交表单
按常规理解,这类非批量处理的规则会让快速点击提交按钮时只会触发一次提交,但下面这个特殊案例却出现了看似矛盾的结果:
export default function App() { const [count, setCount] = useState(0); return ( <div className="App"> <p>{count}</p> <button onClick={() => { setCount(count + 1); }} > click me </button> {Array(99999) .fill(0) .map((_, key) => ( <p key={key}>{Math.random()}</p> ))} </div> ); }
这个组件里,点击按钮会让count加1,同时组件渲染了一个超长列表,导致每次状态更新后的渲染都是耗时的长任务。快速点击两次按钮时,页面会因长任务卡顿,但最终count显示为2,而非预期的1。
原因解析
核心在于React的状态更新与事件回调的执行时机:
- 第一次点击流程:触发回调后,
setCount(0+1)被调用,React将状态更新加入队列。回调执行完毕后,React在微任务阶段完成状态更新(count变为1),随后启动渲染长任务。 - 第二次点击的触发时机:快速点击的第二次事件会被浏览器放入宏任务队列,等待当前的渲染长任务执行完毕。
- 回调的更新:当第一次渲染长任务完成后,组件已经基于
count=1重新渲染,按钮的onClick回调也被更新为捕获了最新count=1的新函数。 - 第二次点击执行:此时浏览器才会处理队列中的第二次点击事件,触发新的回调,调用
setCount(1+1),最终count变为2。
简单来说,第二次点击的事件是在第一次渲染完成后才被执行的,此时回调已经是基于最新状态生成的,所以两次更新会正确累加。
内容的提问来源于stack exchange,提问作者jimin hu
相关产品推荐
相关产品推荐

