React使用useState处理键盘事件时keyup将press数组重置为空问题
问题原因
你遇到的是React Hook的闭包陷阱问题:
- 你给
useEffect传的依赖数组是空数组,所以这个副作用只会在组件首次渲染时执行一次,绑定的keyup、keydown函数都是首次渲染时创建的版本 - 首次渲染时
press状态是初始的空数组,这个值会被闭包捕获,后续无论press怎么更新,你绑定的keyup函数里访问到的press永远是空数组,所以filter之后返回的还是空数组,表现就是每次keyup都会把press清空。
解决方案
最简单的修改方式是把keyUp里的状态更新也改成和keyDown一样的函数式更新写法,直接从回调参数里拿到最新的状态值,不需要依赖闭包中的press:
const keyUp = (e) => { setPress((oldArr) => oldArr.filter((key) => key !== e.key)); };
这种方案不需要修改useEffect的依赖,也不会出现闭包问题,性能最优。
如果要保持你原来的keyUp写法,就需要把press和两个事件处理函数都加到useEffect的依赖数组里,每次press变化时重新绑定事件监听:
useEffect(() => { window.addEventListener("keydown", keyDown); window.addEventListener("keyup", keyUp); return function cleanup() { window.removeEventListener("keydown", keyDown); window.removeEventListener("keyup", keyUp); }; }, [press, keyDown, keyUp]);
不过这种写法会频繁绑定解绑事件,性能不如第一种方案。
可选优化
可以在keyDown里加去重判断,避免同一个按键被重复添加到press数组里:
const keyDown = (e) => { if (e.repeat) return; setPress((oldArr) => { if (oldArr.includes(e.key)) return oldArr; return [...oldArr, e.key]; }); };
内容的提问来源于stack exchange,提问作者Dusttt
相关产品推荐
相关产品推荐

