React中键盘事件更新状态异常问题求助
问题原因
你遇到的是闭包陷阱:useEffect依赖数组为空,仅在组件初始渲染时添加了一次键盘事件监听器,此时绑定的detectKey及内部调用的sendCharacter,捕获的是组件初始状态下的value(即{val: "0"})。后续状态更新导致组件重新渲染时,虽然会生成新的sendCharacter函数,但旧的事件监听器仍持有初始版本的sendCharacter,因此每次按键时,sendCharacter里判断的value.val永远是初始的"0",只会走else分支。
而点击div按钮时,每次组件渲染都会生成新的onClick={() => sendCharacter(1)}回调,这个新回调能捕获到最新的value状态,所以功能正常。
解决方案
有两种常用修复方式:
方案一:使用函数式更新状态
修改sendCharacter里的setValue,改用React函数式更新语法,直接基于上一次状态计算新状态,彻底摆脱对当前闭包中value的依赖:
const sendCharacter = (number) =>{ let str = number.toString(); setValue(prevValue => { if (prevValue.val !== "0") { return { val: prevValue.val.concat(str) }; } else { return { val: str }; } }); };
这种方式无需修改useEffect的依赖,函数式更新会自动获取最新的前一次状态。
方案二:更新useEffect依赖并清理监听器
若要保留原有状态更新逻辑,需让useEffect在sendCharacter变化时重新绑定事件监听器,同时添加清理逻辑避免内存泄漏:
import { useCallback } from 'react'; // 用useCallback缓存函数,仅当依赖变化时生成新函数 const sendCharacter = useCallback((number) =>{ let str = number.toString(); if (value.val != "0") { const conc = value.val.concat(str); setValue({ val: conc }); } else { setValue({ val: str }); } }, [value.val]); // 依赖value.val,状态变化时更新函数 useEffect(() => { const detectKey = (e) => { if (e.key == "1") { sendCharacter(1); } }; document.addEventListener("keyup", detectKey, true); // 组件卸载或依赖变化时移除监听器 return () => { document.removeEventListener("keyup", detectKey, true); }; }, [sendCharacter]); // 依赖sendCharacter,函数更新时重新绑定监听器
两种方案中,方案一更简洁,推荐优先使用函数式更新的方式,从根源上避免闭包导致的旧状态引用问题。
内容的提问来源于stack exchange,提问作者David98761324
相关产品推荐
相关产品推荐

