React useRef 实战:DOM 引用、可变值缓存与规避闭包陈旧值

test12026-10-085 次阅读

useRef 到底解决了什么问题

useState 存的值每次更新都会触发组件重渲染,而有些值我们只是想“记下来”——比如某个 DOM 节点、定时器的句柄、上一次渲染时的 props。useRef 返回的对象在整个组件生命周期里保持同一个引用,读写 .current 不会引发重渲染,这正是它和 useState 的本质区别。

1 引用 DOM 做命令式操作

最常见的用法是把 ref 挂到原生元素上,拿到真实节点后做聚焦、测量、滚动等命令式操作。

import { useEffect, useRef } from "react";

export function AutoFocusInput() {
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  return <input ref={inputRef} placeholder="自动聚焦" />;
}

2 用 ref 保存“上一次”的值

React 不提供内置的“上一次值”钩子,用 useRef 镜像上一次渲染结果即可实现 usePrevious。

import { useEffect, useRef } from "react";

export function usePrevious<T>(value: T): T | undefined {
  const ref = useRef<T | undefined>(undefined);
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

3 规避闭包里的陈旧值

在 setInterval 或事件回调里直接读 state,拿到的是定义函数那一刻的快照。把它同步进 ref,回调里改读 ref.current 就能拿到最新值。

import { useEffect, useRef, useState } from "react";

export function Counter() {
  const [count, setCount] = useState(0);
  const countRef = useRef(count);
  countRef.current = count; // 每次渲染都同步

  useEffect(() => {
    const id = setInterval(() => {
      // 读 ref 而非 count,避免闭包陈旧值
      setCount(countRef.current + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);

  return <div>{count}</div>;
}

4 持有定时器与订阅句柄

把句柄存进 ref,清理函数里统一释放,避免组件卸载后还触发 setState 导致内存泄漏与告警。

export function useInterval(cb: () => void, delay: number) {
  const saved = useRef(cb);
  saved.current = cb;

  useEffect(() => {
    const id = setInterval(() => saved.current(), delay);
    return () => clearInterval(id);
  }, [delay]);
}

什么时候不该用 useRef

  • 值的变化需要反映到界面上,用 useState。
  • 跨组件共享状态,用 Context 或状态库,而不是把 ref 层层透传。
  • 需要触发副作用响应变化,用 useEffect 依赖,而不是在 ref 上手动比较。
T

test1

文章作者

useRef 到底解决了什么问题 useState 存的值每次更新都会触发组件重渲染,而有些值我们只是想“记下来”——...

分类
技术
发布时间
2026-10-08
字数
约 1783 字
阅读
5 次