🚀推荐收藏!三个例子讲解最详细的Ref问题

什么是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节点上,可以是子组件中的某个元素,也可以是整个元素。

区别总结:

  1. 创建方式:

    • 函数组件使用 useRef 钩子创建 ref
    • 类组件使用 React.createRef() 方法创建 ref
  2. 访问方式:

    • 函数组件通过 myRef.current 访问目标。
    • 类组件通过 this.myRef.current 访问目标。
  3. 引入的组件

    • 子组件为函数组件,无法直接获取ref的current,需要借助forwardRef。
    • 子组件为类组件,可以获取ref的current,并且调用子组件中的函数。

感谢您花时间阅读这篇文章!如果觉得有趣或有收获,请关注我的更新,给个喜欢和分享。您的支持是我写作的最大动力!

往期好文推荐

相关推荐
getaxiosluo几秒前
react jsx基本语法,脚手架,父子传参,refs等详解
前端·vue.js·react.js·前端框架·hook·jsx
理想不理想v3 分钟前
vue种ref跟reactive的区别?
前端·javascript·vue.js·webpack·前端框架·node.js·ecmascript
知孤云出岫4 分钟前
web 渗透学习指南——初学者防入狱篇
前端·网络安全·渗透·web
贩卖纯净水.9 分钟前
Chrome调试工具(查看CSS属性)
前端·chrome
测试19981 小时前
2024软件测试面试热点问题
自动化测试·软件测试·python·测试工具·面试·职场和发展·压力测试
小码编匠1 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#
栈老师不回家1 小时前
Vue 计算属性和监听器
前端·javascript·vue.js
AskHarries1 小时前
Java字节码增强库ByteBuddy
java·后端
前端啊龙1 小时前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js
一颗松鼠1 小时前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript