useRef 与 useLayoutEffect:跨越渲染周期与卡在绘制之前
知识背景
useRef 主要有两类用途:① 保存可变容器(.current 修改不触发重渲染);② 绑定 DOM 节点(ref={ref})。useLayoutEffect 与 useEffect 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会拖慢首屏可交互时间,需克制。