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

React UI快照工作原理及定时器中state与ref取值差异解析

React中定时器与state/ref的行为差异解析

正如React官方文档所述,React在渲染时会基于当前状态生成UI快照,任何状态变更都会触发新的重新渲染。

这就导致了一个直观的差异:延迟执行的alert如果打印state变量,会显示alert最初被调度时的state值;但如果打印ref变量,会显示alert触发瞬间的ref值。

示例代码

import { useState, useRef } from 'react';

export default function Chat() {
  const [text, setText] = useState('');
  const dummy = useRef("");
  
 dummy.current = text;
  function handleSend() {
     setTimeout(() => {
      alert('Sending: ' + text);
    }, 5000);
  }
  
  return (
    <>
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
      />
      <button onClick={handleSend}>
        Send
      </button>
    </>
  );
}

在这段代码中,点击Send按钮后修改输入内容,alert会显示点击Send前的text值——这正是React快照机制的体现。但如果将alert中的text替换为dummy.current,alert则会显示alert触发前输入框的最终值。


一、React是如何调度定时器函数并为其赋值的?

React并没有对定时器函数做特殊调度,定时器完全由浏览器的事件循环处理。核心原因在于闭包对变量的捕获机制:

  • 点击Send按钮时,handleSend被调用,此时创建的setTimeout回调函数会捕获当前渲染周期内的text变量——也就是点击瞬间的state值。
  • 后续修改输入框内容会触发state更新,进而触发新的渲染,生成新的text变量(每个渲染周期的state都是独立的常量)。但之前的定时器回调已经绑定到了旧渲染周期的text上,所以5秒后执行时,依然使用那个旧值。

React的渲染机制让每个渲染周期的变量都形成独立闭包,定时器回调会锁定在创建它时所在的渲染周期变量上,这才是导致现象的根本原因,而非React主动调度定时器。

二、state变量与ref变量的赋值逻辑有何不同?

state变量的逻辑

  • state由React管理,每次调用setText更新state时,React会触发新渲染。在新渲染周期中,useState返回更新后的state值——这个值是当前渲染周期的常量,每个渲染周期的state都是独立的,后续的state变更只会生成新的快照,不会修改当前渲染周期内的state变量。
  • 简言之,state是“快照式”的:每个渲染周期拿到的state都是当时状态的固定快照,渲染完成后这个值就不会被后续操作改变。

ref变量的逻辑

  • ref是useRef创建的普通JavaScript对象,它的current属性可以直接修改,且修改不会触发组件重新渲染。
  • ref的current是可变引用:所有渲染周期共享同一个ref对象,无论组件重新渲染多少次,dummy始终指向同一个对象。每次渲染时执行dummy.current = text,都是在更新同一个对象的属性值。定时器回调访问dummy.current时,会直接读取该对象的最新属性值,也就是alert触发瞬间的最新输入内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 13:37:02