React中useState在键盘事件回调函数内取值不正确问题
问题根因
这是典型的React闭包陷阱:你注册键盘事件监听时(代码未贴出的部分,大概率是在依赖为空的useEffect中绑定的keydown事件),绑定的checkKey是组件首次渲染时创建的版本,该函数闭包捕获的永远是首次渲染时currentPage的初始值1,所以每次打印结果都是固定的初始值。
你自定义的状态监听钩子能拿到最新值,是因为它的依赖数组包含currentPage,每次状态变更都会重新执行钩子回调,自然能读取到最新的状态值。
解决方法
你可以选择任意一种方案适配你的业务:
- 方案1:用
useRef同步最新状态,checkKey直接读取ref值,不需要修改事件绑定逻辑
const [currentPage, setCurrentPage] = useState(1); const currentPageRef = useRef(currentPage); // 状态更新时同步到ref useEffect(() => { currentPageRef.current = currentPage; }, [currentPage]) const checkKey = (event) => { if (event.keyCode == '37') { console.log("Key page:" + currentPageRef.current); if(currentPageRef.current > 1) setCurrentPage(prev => prev - 1); } else if (event.keyCode == '39') { console.log("Key page:" + currentPageRef.current); if(currentPageRef.current < 6) setCurrentPage(prev => prev + 1); } }
- 方案2:将checkKey加入事件绑定的依赖数组,每次状态变化重新绑定事件,记得清理旧监听
// 用useCallback包裹checkKey,依赖currentPage,状态变化时生成新的函数 const checkKey = useCallback((event) => { if (event.keyCode == '37') { console.log("Key page:" + currentPage); if(currentPage > 1) setCurrentPage(prev => prev - 1); } else if (event.keyCode == '39') { console.log("Key page:" + currentPage); if(currentPage < 6) setCurrentPage(prev => prev + 1); } }, [currentPage]) // 绑定事件时依赖checkKey,每次函数更新重新绑定 useEffect(() => { window.addEventListener('keydown', checkKey) return () => window.removeEventListener('keydown', checkKey) }, [checkKey])
- 方案3:直接在setCurrentPage的函数式更新参数中读取最新状态,不需要额外依赖
const checkKey = (event) => { if (event.keyCode == '37') { setCurrentPage(prev => { console.log("Key page:" + prev); return prev > 1 ? prev - 1 : prev }) } else if (event.keyCode == '39') { setCurrentPage(prev => { console.log("Key page:" + prev); return prev < 6 ? prev + 1 : prev }) } }
内容的提问来源于stack exchange,提问作者Troublegum
相关产品推荐
相关产品推荐

