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

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

往期好文推荐

相关推荐
muxue1784 分钟前
go语言封装、继承与多态:
开发语言·后端·golang
t_hj16 分钟前
Ajax案例
前端·javascript·ajax
开心码农1号22 分钟前
Go语言中 源文件开头的 // +build 注释的用法
开发语言·后端·golang
北极象23 分钟前
Go主要里程碑版本及其新增特性
开发语言·后端·golang
bigHead-38 分钟前
9. 从《蜀道难》学CSS基础:三种选择器的实战解析
前端·css
lyrhhhhhhhh1 小时前
Spring框架(1)
java·后端·spring
阿里小阿希1 小时前
解决 pnpm dev 运行报错的坎坷历程
前端·node.js
未脱发程序员1 小时前
分享一款开源的图片去重软件 ImageContrastTools,基于Electron和hash算法
前端·javascript·electron
视频砖家2 小时前
Web前端VSCode如何解决打开html页面中文乱码的问题(方法2)
前端·vscode·vscode乱码·vscode中文乱码·vscode中文编码
2401_837088502 小时前
CSS transition过渡属性
前端·css