ref 引用值
ref 保存的是什么?
- 可变容器:
useRef(initialValue)返回具有 current 字段的对象,同一组件的后续渲染得到同一个对象; - 不触发渲染: 修改 current 不会通知 React,因此适合定时器 ID、DOM 节点等不直接决定 UI 的信息;
- 初始参数: 只用于初始化容器,后续传入不同初始值不会自动替换 current;
怎样选择 state 或 ref?
| 需求 | 选择 | 原因 |
|---|---|---|
| 显示点击次数 | state | 变化需要更新 UI |
| 保存定时器 ID | ref | 只为以后清理使用 |
| 保存 DOM 节点 | ref | 需要调用浏览器方法 |
| 渲染期间临时计算 | 局部变量 | 不需要跨渲染保存 |
如何在事件之间保存一个可变值?
- 读写位置: 在事件或 Effect 中读写 ref,把它作为外部操作的句柄,而不是通过它隐藏应该进入 state 的数据;
- 生命周期: ref 只随组件实例存活,组件卸载后需要长期保留的数据应放到更稳定的位置;
import { useRef } from "react";
function DelayedAlert() {
const timeoutRef = useRef(null);
function schedule() {
clearTimeout(timeoutRef.current);
timeoutRef.current = setTimeout(() => alert("完成"), 1000);
}
return <button onClick={schedule}>稍后提示</button>;
}
- 清理责任: 上例只演示事件间保存句柄;需要随卸载取消的任务,应额外由 Effect 清理,完整定时器生命周期见闭包与定时器;
// 放在持有 timeoutRef 的组件顶层,并导入 useEffect。
useEffect(() => {
return () => {
clearTimeout(timeoutRef.current);
};
}, []);
为什么渲染时通常不能读写 current?
- 纯度要求: ref 的变更不会进入 React 的快照机制,渲染中读写会让结果依赖可变外部时序,在重试时产生不一致;
- 初始化例外: 可预测且只在 current 为空时执行的初始化可以使用,详细写法见 Context 与 Ref 参考;
- 不可替代: UI 要显示的值应使用 state,不能通过强制重渲染补救以 ref 代替 state 的设计;