You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 20:39:27