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

React状态快照与重渲染疑问:为何setTimeout读取初始state?

React useState 闭包陷阱解析

你遇到的问题核心是React组件渲染时的闭包捕获机制,下面拆解原因和解决方法:

你的理解误区

你对重渲染和state更新的大部分判断是对的,但忽略了一个关键点:setTimeout的回调函数会通过闭包捕获它被创建时所在渲染周期的state值,和后续重渲染的state无关。

具体到这个组件的执行流程:

  • 初始渲染时,text为空字符串,此时生成的handleSend函数里,setTimeout的回调捕获的是这个初始的text值。
  • 输入框输入内容触发重渲染,text更新为新值,组件会生成新的handleSend函数,这个新函数里的回调会捕获更新后的text。
  • 但如果是在初始渲染阶段点击了Send按钮,此时调用的是初始版本的handleSend,它的回调已经绑定了初始的text。哪怕后续你修改输入框触发了多次重渲染,已经被setTimeout注册的回调不会更新,3秒后执行时依然会弹出初始值。

解决方法:使用useRef

useRef创建的引用对象,它的current属性是可变的,不会被闭包固定住取值,每次访问都会拿到最新的值。修改后的代码如下:

import { useState, useRef } from 'react';

export default function Chat() {
  const [text, setText] = useState('');
  const textRef = useRef('');

  // 同步ref与state的最新值
  textRef.current = text;

  function handleSend() {
    setTimeout(() => {
      alert('Sending: ' + textRef.current);
    }, 3000);
  }

  return (
    <>
      <input
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <button onClick={handleSend}>Send</button>
    </>
  );
}

每次组件重渲染时,textRef.current都会被更新为最新的text值,所以3秒后回调执行时,访问到的就是修改后的最新内容。

内容的提问来源于stack exchange,提问作者DucanoidYoung

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 02:46:01