React中useState的data在handleKeyUp的console.log中为何不更新?
问题原因:闭包导致的旧状态捕获
这是典型的闭包陷阱:
- 组件首次渲染时,
useEffect因依赖为空数组[]仅执行一次,绑定了当时创建的handleKeyUp函数。 - 这个
handleKeyUp会捕获初始状态下的data(即{index: 0, lastChar: null}),后续哪怕data更新,旧的handleKeyUp函数里的data依然是最初的旧值,所以console.log(data)永远输出旧数据。 - 而
setData用了函数式更新((current) => {...}),这种方式能直接拿到最新的状态快照,所以状态能正常更新,页面渲染也不受影响。
解决方案
方案1:用useCallback包裹handleKeyUp并更新依赖
通过useCallback确保handleKeyUp随data更新而刷新,同时让useEffect监听handleKeyUp的变化,重新绑定事件:
import React, { useState, useEffect, useCallback } from "react"; import "./styles.css"; export default function App() { const [data, setData] = useState({ index: 0, lastChar: null }); const handleKeyUp = useCallback((e) => { console.log(data); setData((current) => { return { index: current.index + 1, lastChar: e.key }; }); }, [data]); useEffect(() => { document.addEventListener("keyup", handleKeyUp); return () => { document.removeEventListener("keyup", handleKeyUp); }; }, [handleKeyUp]); return ( <div className="App"> {data.index} {data.lastChar} <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> </div> ); }
方案2:用useRef保存最新状态
通过useRef跟踪实时状态,避免闭包捕获旧值,同时不用频繁绑定事件:
import React, { useState, useEffect, useRef } from "react"; import "./styles.css"; export default function App() { const [data, setData] = useState({ index: 0, lastChar: null }); const dataRef = useRef(data); // 每次data更新时同步ref的值 useEffect(() => { dataRef.current = data; }, [data]); const handleKeyUp = (e) => { console.log(dataRef.current); // 访问最新状态 setData((current) => { return { index: current.index + 1, lastChar: e.key }; }); }; useEffect(() => { document.addEventListener("keyup", handleKeyUp); return () => { document.removeEventListener("keyup", handleKeyUp); }; }, []); return ( <div className="App"> {data.index} {data.lastChar} <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> </div> ); }
方案3:直接在useEffect内定义事件处理函数
把handleKeyUp写在useEffect内部,让它每次随data更新重新创建,自然捕获最新状态:
import React, { useState, useEffect } from "react"; import "./styles.css"; export default function App() { const [data, setData] = useState({ index: 0, lastChar: null }); useEffect(() => { const handleKeyUp = (e) => { console.log(data); setData((current) => { return { index: current.index + 1, lastChar: e.key }; }); }; document.addEventListener("keyup", handleKeyUp); return () => { document.removeEventListener("keyup", handleKeyUp); }; }, [data]); return ( <div className="App"> {data.index} {data.lastChar} <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> </div> ); }
内容的提问来源于stack exchange,提问作者Razvan Nuna
相关产品推荐
相关产品推荐

