在 React 中,ref
(引用)是一种用于直接访问 DOM 节点或 React 组件实例的机制。它突破了 React 的声明式范式,允许开发者直接操作底层 DOM 或调用子组件的方法。以下是 ref
的核心作用和用法:
一、ref
的核心作用
-
直接操作 DOM 元素
- 例如:获取输入框焦点、测量元素尺寸、触发动画、集成第三方 DOM 库(如 D3.js)等。
- 示例:自动聚焦输入框:
const inputRef = useRef(null); useEffect(() => { inputRef.current.focus() }, []); return <input ref={inputRef} />;
-
访问子组件的实例方法
- 在类组件中,通过
ref
调用子组件的方法(函数组件需结合useImperativeHandle
)。 - 示例:调用子组件的
play
方法:// 父组件 const videoRef = useRef(null); const play = () => videoRef.current.play(); return <VideoPlayer ref={videoRef} />; // 子组件(通过 forwardRef + useImperativeHandle 暴露方法) const VideoPlayer = forwardRef((props, ref) => { const playerRef = useRef(); useImperativeHandle(ref, () => ({ play: () => playerRef.current.play(), pause: () => playerRef.current.pause(), })); return <video ref={playerRef} />; });
- 在类组件中,通过
-
存储可变值(无需触发渲染)
- 通过
useRef
保存一个可变值(类似类组件的实例变量),修改它不会触发重新渲染。 - 示例:记录上一次的状态:
const prevCountRef = useRef(); useEffect(() => { prevCountRef.current = count; // 更新 ref 不会触发渲染 });
- 通过
二、ref
的 4 种创建方式
-
字符串 ref(已废弃,不推荐)
<input ref="myInput" /> // 通过 this.refs.myInput 访问
-
回调函数 ref
<input ref={(el) => { this.inputElement = el; }} />
-
React.createRef()
(类组件)class MyComponent extends React.Component { inputRef = React.createRef(); render() { return <input ref={this.inputRef} />; } }
-
useRef
Hook(函数组件)function MyComponent() { const inputRef = useRef(null); return <input ref={inputRef} />; }
三、ref
的注意事项
-
避免滥用
- 优先使用 React 的声明式数据流(如通过
state
和props
控制 UI),仅在必要时使用ref
。
- 优先使用 React 的声明式数据流(如通过
-
函数组件默认不暴露 ref
- 需要使用
forwardRef
转发 ref 到子组件的 DOM 元素:const MyInput = forwardRef((props, ref) => ( <input ref={ref} {...props} /> ));
- 需要使用
-
不可在渲染期间访问 ref
ref.current
在组件挂载后才会赋值,渲染期间(如return
前)访问可能为null
。
四、常见使用场景
场景 | 示例 |
---|---|
表单控件焦点管理 | 输入框自动聚焦 |
媒体播放控制 | 播放/暂停视频或音频 |
动画触发 | 直接调用 DOM 动画 API |
第三方库集成 | D3.js 图表、地图库 |
测量元素尺寸 | 获取元素的宽高或位置 |
保存计时器 ID | 清除 setInterval |
总结
ref
是 React 的逃生舱:在需要直接操作 DOM 或子组件时使用,但应谨慎。- 函数组件优先用
useRef
,类组件用createRef
或回调 ref。 - 结合
forwardRef
和useImperativeHandle
实现更精细的 ref 控制。