🚀推荐收藏!三个例子讲解最详细的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,并且调用子组件中的函数。

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

往期好文推荐

相关推荐
00后程序员张1 小时前
Fiddler抓包工具使用教程,代理设置与调试方法实战解析(含配置技巧)
前端·测试工具·ios·小程序·fiddler·uni-app·webview
.格子衫.5 小时前
Spring Boot 原理篇
java·spring boot·后端
2301_768350237 小时前
Vue第二期:组件及组件化和组件的生命周期
前端·javascript·vue.js
Jabes.yang7 小时前
Java求职面试实战:从Spring Boot到微服务架构的技术探讨
java·数据库·spring boot·微服务·面试·消息队列·互联网大厂
聪明的笨猪猪8 小时前
Java Redis “高可用 — 主从复制”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
兮动人8 小时前
Spring Bean耗时分析工具
java·后端·spring·bean耗时分析工具
倔强青铜三8 小时前
苦练Python第64天:从零掌握多线程,threading模块全面指南
人工智能·python·面试
华洛8 小时前
公开一个AI产品的商业逻辑与设计方案——AI带来的涂色卡自由
前端·后端·产品
明远湖之鱼8 小时前
opentype.js 使用与文字渲染
前端·svg·字体
追逐时光者8 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 57 期(2025年10.1-10.12)
后端·.net