为何我的eventListener仅在选中对应屏幕后生效一次?
问题分析与解决方案
你的代码存在三个核心问题,导致键盘切换逻辑只能生效一次:
- 闭包捕获旧状态:
onKeydown4里直接引用的store是组件初始渲染时的闭包值,后续store更新后,事件处理函数里的store还是旧值,切换逻辑会失效。 - 未清理全局事件监听:进入Screen4时添加了键盘监听,但离开页面时没移除,不仅会造成内存泄漏,还可能在其他页面触发错误逻辑。
- useEffect依赖缺失:
useEffect的依赖数组是空的,只有组件挂载时执行一次,当props.name变化(比如离开再回到Screen4),不会重新执行监听的添加逻辑。
修复后的代码如下:
// 用useCallback包裹事件函数,依赖store保证状态更新时函数能获取最新值 const onKeydown4 = useCallback((e) => { if (e.key === "w") { // 使用函数式更新,直接获取最新的store状态,规避闭包问题 setStore(prevStore => ({ ...prevStore, // 不要遗漏store里的其他状态属性 bigVariant: prevStore.bigVariant === "variant_big_A" ? "variant_big_B" : "variant_big_A" })); } }, [store]); useEffect(() => { if (props.name === "Screen 4") { window.addEventListener("keydown", onKeydown4); // 清理函数:离开Screen4时自动移除监听 return () => { window.removeEventListener("keydown", onKeydown4); }; } }, [props.name, onKeydown4]); // 补充依赖,props切换或函数更新时重新执行逻辑 return <Component {...props} />;
关键修复点说明:
- 函数式更新setStore:通过
prevStore参数获取最新状态,彻底解决闭包导致的旧值问题。 - useCallback缓存事件函数:保证
onKeydown4的引用稳定,同时依赖store,状态更新时函数会重新生成,确保能拿到最新状态。 - useEffect清理函数:离开Screen4时自动移除全局监听,避免副作用残留。
- 补充useEffect依赖:当
props.name切换或事件函数更新时,重新执行监听的添加/移除逻辑。
内容的提问来源于stack exchange,提问作者Cublaiiii
相关产品推荐
相关产品推荐

