React中Keyup事件监听器无法获取更新后的状态
问题原因与解决方案
问题根源
你的代码触发了React的闭包陷阱:
useEffect的依赖数组为空,仅在组件首次挂载时执行一次,此时handleShortcutKeys函数捕获的是name的初始值("John Doe")。- 当你通过输入框更新
name时,组件会重新渲染,但由于useEffect的依赖未变化,不会重新创建handleShortcutKeys,事件监听始终调用旧函数,拿到的自然是初始状态。
解决方案一:添加状态到依赖数组
将name加入useEffect的依赖数组,每次name更新时,useEffect会重新执行,移除旧监听并绑定新的监听函数,新函数将捕获最新的name值。
修改后的核心代码:
useEffect(() => { let handleShortcutKeys = (evt) => { if (evt.ctrlKey && evt.key === "Enter") { alert(name); } }; window.addEventListener("keyup", handleShortcutKeys); return () => { window.removeEventListener("keyup", handleShortcutKeys); }; }, [name]); // 添加name作为依赖
解决方案二:使用useRef保存最新状态
如果不想频繁绑定/解绑事件监听,可以用useRef保存最新的name值,ref.current始终指向当前最新状态,无需依赖数组变化。
修改后的完整代码:
import "./styles.css"; import { useState, useEffect, useRef } from "react"; export default function App() { let [name, setName] = useState("John Doe"); const nameRef = useRef(name); // 同步ref与最新状态 useEffect(() => { nameRef.current = name; }, [name]); useEffect(() => { let handleShortcutKeys = (evt) => { if (evt.ctrlKey && evt.key === "Enter") { alert(nameRef.current); // 访问最新状态 } }; window.addEventListener("keyup", handleShortcutKeys); return () => { window.removeEventListener("keyup", handleShortcutKeys); }; }, []); return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> <input type="text" placeholder="enter something..." value={name} onChange={(evt) => setName(evt.target.value)} /> <br /> <br /> <button onClick={() => alert(name)}>Alert Name</button> </div> ); }
方案对比
- 方案一:实现简单,适合状态更新不频繁的场景,但每次状态变化都会重新绑定事件。
- 方案二:仅绑定一次事件监听,性能更优,适合状态频繁更新的场景(比如在线IDE的代码编辑场景)。
内容的提问来源于stack exchange,提问作者vrintle
相关产品推荐
相关产品推荐

