什么是ref
在 React 中,对于函数组件和类组件,通过
ref
获取非真实 DOM 元素(如类组件实例或函数组件的内部实例)的方式是一致的。无论是函数组件还是类组件,ref
的作用并不仅限于获取真实的 DOM 元素,它也可以用于引用组件的实例或其他 React 元素。
问题复现
js
const Test = () => { return <div>Hello, World!</div> }
开发中遇到ref挂载到想要获取的组件上,上面的Test组件,这是我们引入封装好的一个测试组件,这里将ref挂载一下,然后在打印,看看targetRef能不能获取到对应的值
js
...
toScorll = ()=>{
console.log('targetRef',this.targetRef)
}
<Table
columns={this.state.columns}
dataSource={this.state.data}
loading={loading}
/>
</div>
</Col>
</Row>
<Test ref={this.targetRef} ></Test>
如果目标是一个组件,当打印出targetRef的时候,会显示current是null的结果,引入的Test是一个函数式组件,而不是一个类组件,当Test为类组件的时候,又能成功获取。所以针对这个问题做一下实验区分一下。
ref绑定在类组件上面
这里MyComponent为类组件
js
import React, { useState, useCallback,Component } from 'react';
import ReactDom from 'react-dom';
class MyComponent extends Component {
doSomething() {
// 在类组件中通过 ref 使用
console.log("Doing something!");
}
render() {
return <div>Hello, World!</div>;
}
}
class App extends React.Component {
constructor(props) {
super(props);
// 创建一个 ref 对象
this.myComponentRef = React.createRef();
}
componentDidMount() {
// 在组件挂载后,通过 ref 访问 MyComponent 的实例
console.log(this.myComponentRef.current); // 输出 MyComponent 的实例
this.myComponentRef.current.doSomething(); // 调用 MyComponent 中的方法
}
render() {
// 将 ref 对象传递给 MyComponent
return <MyComponent ref={this.myComponentRef} />;
}
}
export default App;
ReactDom.render(<App />, document.getElementById('app'));
当类组件引入的自组件仍然是类组件,可以直接打印出无状态组件的各个属性,并且可以直接调用子组件中的函数
绑定在函数组件上
这里MyComponent为函数组件
js
import React, { useState, useCallback,Component } from 'react';
import ReactDom from 'react-dom';
const MyComponent = () => {
return <div>Hello, World!</div>
}
class App extends React.Component {
constructor(props) {
super(props);
// 创建一个 ref 对象
this.myComponentRef = React.createRef();
}
componentDidMount() {
// 在组件挂载后,通过 ref 访问 MyComponent 的实例
console.log('componentDidMount',this.myComponentRef.current); // 输出 MyComponent 的实例
this.myComponentRef.current.doSomething(); // 调用 MyComponent 中的方法
}
render() {
// 将 ref 对象传递给 MyComponent
return <MyComponent ref={this.myComponentRef} />;
}
}
export default App;
ReactDom.render(<App />, document.getElementById('app'));
绑定在函数组件上,获取不到无状态组件的Dom,current打印出来的是null,并且也访问不到其中的dosomething函数
js
render() {
// 将 ref 对象传递给 MyComponent
return <div ref={this.myComponentRef} >2323</div>
// <MyComponent ref={this.myComponentRef} />;
}
如果将上面的代码进行改动,如果获取的是真实的dom元素
可以看到能打印出正确的元素
上方两个实验都是在类组件中引入
非真实元素,为了实验的准确性,笔者再次尝试使用函数组件为父组件去做引入
父组件是函数组件 引入函数式子组件
js
import ReactDom from 'react-dom';
import React, { useRef, useEffect } from 'react';
const MyComponent = () => {
return <div>Hello, World!</div>
}
const App = () => {
const myRef = useRef();
useEffect(() => {
// 使用 myRef.current 访问目标
console.log('myRef.current',myRef.current);
}, []);
return <MyComponent ref={myRef}>This is a functional component</MyComponent>;
};
export default App;
即使是在函数组件中引入函数组件,同样获取不到current的情况
父组件是函数组件 引入类子组件
js
import React, { useState, useCallback,Component,useEffect,useRef} from 'react';
import ReactDom from 'react-dom';
class MyComponent extends Component {
doSomething() {
// 在类组件中通过 ref 使用
console.log("Doing something!");
}
render() {
return <div>Hello, World!</div>;
}
}
const App = () => {
const myRef = useRef();
useEffect(() => {
// 使用 myRef.current 访问目标
console.log('myRef.current',myRef.current);
}, []);
return <MyComponent ref={myRef}>This is a functional component</MyComponent>;
};
export default App;
ReactDom.render(<App />, document.getElementById('app'));
如何解决函数式子组件无法获取current的问题
答案是使用forwardRef
forwardRef
是 React 提供的一个 API,用于在函数组件中向子组件转发ref
。这在你想在函数组件中直接访问子组件实例时非常有用。通常情况下,函数组件不能直接接收ref
,但通过forwardRef
,你可以将ref
转发给函数组件内部的某个元素或组件。
js
import React, { useState, useCallback,Component,useEffect,useRef,forwardRef} from 'react';
import ReactDom from 'react-dom';
const MyComponent = forwardRef((props,ref) => {
return <div ref={ref}>Hello, World!</div>;
})
const App = () => {
const myRef = useRef();
useEffect(() => {
// 使用 myRef.current 访问目标
console.log('myRef.current',myRef.current);
}, []);
return <>
<MyComponent ref={myRef}>This is a functional component</MyComponent>;
</>
};
export default App;
ReactDom.render(<App />, document.getElementById('app'));
对于子组件MyComponent,我们只需要给它套上一层forwardRef,引入的时候不用做任何修改,也就是上方代码的14行不用做任何改动,像之前一样传入即可,再将传入的ref,转发到你想要获取的dom节点上,可以是子组件中的某个元素,也可以是整个元素。
区别总结:
-
创建方式:
- 函数组件使用
useRef
钩子创建ref
。 - 类组件使用
React.createRef()
方法创建ref
。
- 函数组件使用
-
访问方式:
- 函数组件通过
myRef.current
访问目标。 - 类组件通过
this.myRef.current
访问目标。
- 函数组件通过
-
引入的组件
- 子组件为函数组件,无法直接获取ref的current,需要借助forwardRef。
- 子组件为类组件,可以获取ref的current,并且调用子组件中的函数。