React中setTimeout与闭包结合时count状态不更新问题如何解决?
你的判断完全正确。
问题原因
这是React函数组件典型的闭包捕获问题:函数组件每一次渲染都会生成独立的执行上下文,当你点击「Show alert」按钮时,触发的handleAlertClick属于当前渲染批次,内部的setTimeout回调捕获的就是当前批次的count值。后续点击「Click me」更新count只会触发组件重新渲染,生成新的count变量,不会影响已经触发的setTimeout回调里已经捕获的旧值。
修复方案
最常用的解决方案是使用useRef存储count的最新引用,useRef返回的对象在组件的整个生命周期内共享同一个引用,修改current属性不会触发组件重渲染,也不会被闭包捕获限制。修复后的代码如下:
import React, { useState, useRef, useEffect } from 'react' function Root() { const [count, setCount] = useState(0) // 定义ref存储最新的count值 const countRef = useRef(count) // 每次count更新时同步到ref useEffect(() => { countRef.current = count }, [count]) function handleAlertClick() { setTimeout(() => { // 读取ref的current属性获取最新值 alert(`You clicked ${countRef.current} times`) }, 2500) } return ( <Container> <Column> <h4>Closures</h4> <p>You clicked {count} times</p> <button type="button" onClick={() => setCount(counter => counter + 1)}> Click me </button> <button type="button" onClick={handleAlertClick}> Show alert </button> </Column> </Container> ) } export default Root
如果使用的是React 18及以上版本,也可以使用内置的useEvent钩子封装定时器逻辑,不需要手动维护ref同步:
import React, { useState, unstable_useEvent as useEvent } from 'react' function Root() { const [count, setCount] = useState(0) const showAlert = useEvent(() => { alert(`You clicked ${count} times`) }) function handleAlertClick() { setTimeout(() => { showAlert() }, 2500) } // 渲染部分与原代码一致,此处省略 }
内容的提问来源于stack exchange,提问作者jakhando
相关产品推荐
相关产品推荐

