Skip to content

useRef 与 useLayoutEffect:跨越渲染周期与卡在绘制之前

知识背景

useRef 主要有两类用途:① 保存可变容器.current 修改不触发重渲染);② 绑定 DOM 节点ref={ref})。
useLayoutEffectuseEffect API 相似,但执行时机在 DOM 变更之后、浏览器绘制之前,同步执行,会阻塞视觉更新,适合测量布局与避免闪烁。


知识详解与通俗解释

1. useRef 当「闭包保险箱」

jsx
const latest = useRef(props.id);
latest.current = props.id; // 每次渲染更新,不触发渲染

useEffect(() => {
  const t = setInterval(() => {
    console.log(latest.current); // 始终最新
  }, 1000);
  return () => clearInterval(t);
}, []);

对比:若只用 props.id 进依赖,interval 会反复重建;只用 state 闭包会过期。ref 适合存「逻辑上要读最新、但不想放进依赖」的值(需审慎,别用来绕过合理的依赖分析)。

2. DOM ref

jsx
const el = useRef(null);
useLayoutEffect(() => {
  const { width } = el.current.getBoundingClientRect();
  // ...
}, []);
return <div ref={el} />;

函数组件上要用 forwardRef 才能把 ref 传给子组件根 DOM。

3. useLayoutEffect 何时用?

  • 需要读 DOM 几何信息再同步改样式(如 Tooltip 定位)。
  • 需要在用户看见之前完成修正,避免一闪而过的错位。

SSR 场景:useLayoutEffect 在服务端不跑,可能报警告;可用 useEffect 替代或动态导入仅客户端组件。

4. 与 useEffect 的选择

默认 useEffect;只有出现可见闪烁、测量依赖同步再考虑 useLayoutEffect


总结

  • useRef:可变 .current、DOM 引用;改 ref 不渲染
  • useLayoutEffect:同步、绘制前;适合测量与消除闪烁。
  • 滥用 useLayoutEffect 会拖慢首屏可交互时间,需克制。