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

React中如何实现并发安全且取值正确的useLatest Hook?

支持并发安全且始终返回正确值的 useLatest 实现方案

首先明确两种常见实现的核心缺陷:

  • 渲染阶段直接更新ref的方案:React 18 并发模式下,可中断、可回退的渲染过程会直接修改全局共享的ref对象,未提交的渲染更新会污染ref状态,导致并发场景下出现非预期的取值错误。
  • 依赖useEffect更新ref的方案:useEffect在渲染提交完成后才执行,渲染阶段、提交前的同步逻辑都只能拿到上一次提交的旧值,不满足渲染期间取最新值的需求。

你自己尝试的实现思路是合理的,通过区分临时渲染值和已提交值解决了并发污染的问题,但存在的两个缺陷核心根因是每次渲染返回新的ref对象:旧闭包捕获的旧ref对象里的startingUpdateCount是旧渲染批次的值,自然会读取到旧的已提交值。


优化后的稳定实现

import { useRef, useLayoutEffect } from 'react';

function useLatest<T>(value: T): { readonly current: T } {
  const innerRef = useRef({
    // 存储已提交到视图的正式值
    committedValue: value,
    // 存储当前渲染批次的最新临时值
    latestValue: value,
  });

  // 渲染阶段先更新当前批次的临时最新值
  innerRef.current.latestValue = value;

  useLayoutEffect(() => {
    // 只有渲染提交成功后,才更新正式的已提交值
    innerRef.current.committedValue = value;
  }, [value]);

  // 用useRef保证返回的代理对象永远稳定,不会随渲染变化
  const proxyRef = useRef({
    get current() {
      // 任意场景下都优先取最新的渲染值,渲染阶段取当前批次的值,提交后latestValue和committedValue自动对齐
      return innerRef.current.latestValue;
    }
  }).current;

  return proxyRef;
}

如果需要支持手动修改current值,可以调整为兼容MutableRefObject的版本:

import { useRef, useLayoutEffect, MutableRefObject } from 'react';

function useLatest<T>(value: T): MutableRefObject<T> {
  const innerRef = useRef({
    committedValue: value,
    latestValue: value,
  });

  innerRef.current.latestValue = value;

  useLayoutEffect(() => {
    innerRef.current.committedValue = value;
  }, [value]);

  const proxyRef = useRef({
    get current() {
      return innerRef.current.latestValue;
    },
    set current(newVal: T) {
      innerRef.current.latestValue = newVal;
    }
  }).current;

  return proxyRef;
}

方案验证

  • 并发安全:只有渲染提交后的useLayoutEffect才会更新已提交值,未提交的渲染哪怕临时修改了latestValue,要么后续重新渲染会用对应批次的value覆盖,要么不会有外部逻辑在未提交阶段读取(React保证事件、副作用都只会在渲染提交后执行),不会出现ref被未提交更新污染的问题。
  • 取值永远正确:
    • 渲染阶段读取:直接拿到当前渲染批次传入的最新value,不会出现旧值问题
    • 提交后事件、副作用中读取:拿到的是最新提交的value,和视图状态完全对齐
  • 闭包兼容:返回的代理对象永远稳定,旧闭包捕获该对象后,后续读取永远会拿到最新的value,解决了你提到的function1读取旧值的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 09:27:04