目录
- [1. ref 的基本概念](#1. ref 的基本概念)
- [2. 如何使用 ref](#2. 如何使用 ref)
-
- [2.1 基本用法](#2.1 基本用法)
- [2.2 类组件使用 createRef](#2.2 类组件使用 createRef)
- [3. forwardRef 转发 ref](#3. forwardRef 转发 ref)
- [4. ref 的应用场景](#4. ref 的应用场景)
- [5. ref 和函数组件](#5. ref 和函数组件)
- 总结
在 React 中,ref
(引用)用于访问 DOM 元素或类组件实例。它允许我们直接与元素进行交互,而不需要依赖 React 的数据流。
下面是关于 React 元素接收 ref
的详细解释。
1. ref 的基本概念
React 中的 ref
主要有两种类型:
- 字符串
ref
:React 16.3 之前的用法,已经被废弃,不推荐使用。 - 回调
ref
:提供一个函数,将ref
作为参数传递给该函数。可以访问 DOM 元素或组件实例。 createRef()
或者useRef()
:推荐的现代用法,允许创建一个ref
对象,该对象可以用于访问 DOM 元素或类组件实例。
2. 如何使用 ref
2.1 基本用法
函数组件为例:
javascript
import React, { useRef, useEffect } from 'react';
const MyComponent = () => {
const myRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (myRef.current) {
myRef.current.focus(); // 聚焦到 input 元素
}
}, []);
return <input ref={myRef} />;
};
export default MyComponent;
在这个例子中,我们使用了 useRef
钩子创建一个 ref
,并将它传递给一个 <input>
元素。这样,myRef.current
就指向该 input
元素,我们可以在 useEffect
中直接操作 DOM。
这里有一个技巧,前面提过 ref 支持接收回调函数,因此我们可以让代码更加简洁:
typescript
import React, { useCallback } from 'react';
const MyComponent = () => {
const myRef = useCallback((node) => node?.focus(), [])
return <input ref={myRef} />;
};
export default MyComponent;
它的工作原理如下:
- 当 DOM 节点添加到屏幕时,React 会以 DOM 节点作为参数调用该函数。
- 当 DOM 节点被移除时,React 会以 null 调用该函数。
2.2 类组件使用 createRef
在类组件中,我们通常使用 React.createRef()
来创建 ref
。
typescript
import React, { Component } from 'react';
class MyComponent extends Component {
private myRef = React.createRef<HTMLInputElement>();
componentDidMount() {
if (this.myRef.current) {
this.myRef.current.focus();
}
}
render() {
return <input ref={this.myRef} />;
}
}
export default MyComponent;
3. forwardRef 转发 ref
如果你要在函数组件中使用 ref
,并且希望该 ref
被传递到子组件中,你需要使用 React.forwardRef
来转发 ref
。
typescript
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
interface CustomInputProps {
label: string;
}
const CustomInput = forwardRef<HTMLInputElement, CustomInputProps>((props, ref) => {
const inputRef = useRef<HTMLInputElement>(null);
// 允许父组件操作子组件的方法
useImperativeHandle(ref, () => ({
focus: () => {
if (inputRef.current) {
inputRef.current.focus();
}
}
}));
return (
<div>
<label>{props.label}</label>
<input ref={inputRef} />
</div>
);
});
export default CustomInput;
在这个例子中,CustomInput
是一个函数组件,它通过 forwardRef
接收外部的 ref
。我们通过 useImperativeHandle
将自定义的方法(如 focus
)暴露给父组件。
在父组件使用时:
typescript
import React, { useRef } from 'react';
import CustomInput from './CustomInput';
const ParentComponent = () => {
const inputRef = useRef<{ focus: () => void }>(null);
return (
<div>
<CustomInput ref={inputRef} label="Username" />
<button onClick={() => inputRef.current?.focus()}>Focus Input</button>
</div>
);
};
export default ParentComponent;
4. ref 的应用场景
- 访问 DOM 元素 :如上述例子,
ref
允许你直接访问 DOM 元素并操作其属性,例如聚焦、滚动、选择文本等。 - 与第三方库集成 :很多第三方库(如 D3.js 或 jQuery)需要直接操作 DOM 元素,这时
ref
就非常有用。 - 获取组件实例 :虽然不推荐直接访问组件实例,但如果需要,可以通过
ref
来访问类组件的实例方法。
5. ref 和函数组件
默认情况下,ref
只能用于类组件或 DOM 元素。如果你尝试将 ref
直接传递给一个函数组件,React 会给出警告,表示函数组件无法接收 ref
。为了让函数组件能够接收 ref
,你需要使用 React.forwardRef
。
总结
ref
是 React 中访问 DOM 或组件实例的一种方式。- 函数组件无法直接使用
ref
,需要使用React.forwardRef
转发ref
。 ref
可以配合useImperativeHandle
定制暴露给父组件的接口。ref
对于与第三方库集成和直接操作 DOM 元素非常有用。