React中无Stale State捕获按键:实体/虚拟键盘切换异常修复
实体键盘输入异常的原因与修复
异常原因
问题出在Keyboard组件的useEffect依赖项为空数组:组件挂载时绑定的keydown事件监听器,会始终引用组件初始化时的props.onClick函数。而初始的onClick函数捕获的是App组件中isKeyboardEnabled的初始值false,即便后续通过开关修改了isKeyboardEnabled的状态,事件监听器里的函数依然使用旧的闭包值,导致按下实体键盘时触发旧逻辑,输出“键盘未启用”日志。
修复方案
有两种可行的修复方式:
方式一:更新useEffect的依赖项
让Keyboard组件的useEffect监听props.onClick的变化,当onClick更新时重新绑定事件监听器,确保事件处理函数始终使用最新的状态值:
import React, { useState, useEffect } from "react"; export default function App(props) { const [isKeyboardEnabled, setIsKeyboardEnabled] = useState(false); const [str, setStr] = useState(""); const onClick = (v) => { if (isKeyboardEnabled) { setStr((str) => str + v); } else { console.log(`keyboard not enabled ("${v}")`); } }; return ( <div> <div>str: "{str}"</div> <br /> <Keyboard onClick={onClick} /> <br /> <button onClick={() => setIsKeyboardEnabled((v) => !v)}> isKeyboardEnabled: {isKeyboardEnabled.toString()} </button> </div> ); } function Keyboard(props) { const handleKeyDown = (v) => { if (!/^[a-z]$/.test(v)) return; props.onClick(v); }; useEffect(() => { const fn = (e) => handleKeyDown(e.key); document.addEventListener("keydown", fn); return () => { document.removeEventListener("keydown", fn); }; }, [props.onClick]); // 添加上props.onClick作为依赖项 return ( <div> {[..."abcdefghijklmnopqrstuvwxyz"].map((v, i) => ( <button key={i} onClick={() => handleKeyDown(v)}>{v}</button> ))} </div> ); }
方式二:将启用状态下移到Keyboard组件
把isKeyboardEnabled状态传递给Keyboard组件,在组件内部做启用判断,避免闭包捕获旧值的问题:
import React, { useState, useEffect } from "react"; export default function App(props) { const [isKeyboardEnabled, setIsKeyboardEnabled] = useState(false); const [str, setStr] = useState(""); const addChar = (v) => { setStr((str) => str + v); }; return ( <div> <div>str: "{str}"</div> <br /> <Keyboard onCharPress={addChar} isEnabled={isKeyboardEnabled} /> <br /> <button onClick={() => setIsKeyboardEnabled((v) => !v)}> isKeyboardEnabled: {isKeyboardEnabled.toString()} </button> </div> ); } function Keyboard({ onCharPress, isEnabled }) { const handleKeyDown = (v) => { if (!/^[a-z]$/.test(v)) return; if (isEnabled) { onCharPress(v); } else { console.log(`keyboard not enabled ("${v}")`); } }; useEffect(() => { const fn = (e) => handleKeyDown(e.key); document.addEventListener("keydown", fn); return () => { document.removeEventListener("keydown", fn); }; }, [isEnabled, onCharPress]); // 监听isEnabled和onCharPress的变化 return ( <div> {[..."abcdefghijklmnopqrstuvwxyz"].map((v, i) => ( <button key={i} onClick={() => handleKeyDown(v)}>{v}</button> ))} </div> ); }
内容的提问来源于stack exchange,提问作者Nathan Chu
相关产品推荐
相关产品推荐

