一次性搞定React的Ref功能

React的Ref功能

1.String Ref

String Ref是个过时的API。因为String类型的Ref存在一些问题,将在未来的某个版本中被遗弃,不建议使用。

使用方式:this.refs.XXX获取DOM元素节点:

获取普通标签:

import React, { Component } from 'react';
class App extends Component {
componentDidMount() {
console.log('this.refs.XXX');
console.log(this.refs.h1Ref);
}
render() {
return <h1 ref='h1Ref'>Hello World!</h1>
}
}
export default App;

复制代码打印结果:

this.refs.xxx
<h1>Hello World!</h1>

获取react组件:此时可以调用组件上的方法

import React, { Component } from 'react';
class App extends Component {
componentDidMount() {
console.log(this.refs.childRef);
this.refs.childRef.handleLog(); // Child Component }
render() {
return (
<div>
<h1>Hello World!</h1>
<Child ref='childRef' count='1' />
</div>
)
}
}
class Child extends Component {
handleLog = () => {
console.log('Child Component');
}
render() {
const { count } = this.props;
return <h2>count: { count }</h2>
}
}
export default App

// 结果
组件的引用,包括上面挂载的函数和组件props等

2.Callback Ref

Callback Ref能助你更精细地控制何时 refs

被设置和解除,传递一个函数。这个函数中接受 React 组件实例或 HTML DOM 元素作为参数。

React 将在组件挂载时,会调用 ref 回调函数并传入 DOM 元素,当卸载时调用它并传入 null。在 componentDidMount 或 componentDidUpdate 触发前,React 会保证 refs 一定是最新的。

使用方式:ref={element => (this.eleref = element)}

获取DOM元素节点:

import React, { Component } from "react";
class App extends Component {
componentDidMount() {
console.log("Callback Ref");
console.log(this.h1Ref);
}
render() {
return (
<div>
<h1 ref={element => (this.h1Ref = element)}>Hello World!</h1>
</div>
);
}
}
export default App;
// 打印结果
Callback Ref
<h1>Hello World!</h1>

获取子组件实例:【同上可调用方法】

import React, { Component } from "react";
class App extends Component {
componentDidMount() {
console.log("Callback Ref");
console.log(this.childRef);
this.childRef.handleLog();
}
render() {
return (
<div>
<h1>Hello World!</h1>
<Child ref={component => (this.childRef = component)} count="1" />
</div>
);
}
}
class Child extends Component {
handleLog = () => {
console.log("Child Component");
};
render() {
const { props } = this;
return <h1>count: {props.count}</h1>;
}
}
export default App;

3.Create Ref

该功能是React16.3中发布的,并且在类组件中推荐使用

使用React.createRef()创建Refs,通过ref附加到组件中,对该节点的引用通过ref的current属性访问

React在组件挂载时给current传入DOM元素,并在组件卸载时传入null,ref会在生命周期函数前更新完成

使用方法和前两种类似,这里就不一一列举了

import React, { Component, createRef} from "react";
class App extends Component {
constructor(props) {
super(props);
this.h1Ref = createRef();
}
componentDidMount() {
console.log("React.createRef()");
console.log(this.h1Ref.current);
}
render() {
return <h1 ref={this.h1Ref}>Hello World!</h1>;
}
}
export default App;

4.useRef

当在函数组件中使用前三种ref,会抛出以下错误:Uncaught Invariant Violation: Function components cannot have refs. Did you mean to use React.forwardRef()?

原因:函数组件和class组件根本区别是,函数组件没有实例,所以无法使用实例对象,取而代之的为useRef,或使用forwardRef

作用:

获取DOM节点
获取组件实例
渲染周期之间共享数据存储(state修改会触发重新渲染,所以不能跨周期共享)
使用方法:与createRef类似,挂在current上

import React, { useEffect, useRef } from 'react';
function App() {
const h1Ref = useRef();
useEffect(() => {
console.log('useRef')
console.log(h1Ref.current)
}, [])
return <h1 ref={h1Ref}>Hello World!</h1>
}
export default App;

5 不同渲染周期之间的数据共享

该情况主要解决一类问题:类组件中,函数组件中的属性无法跨渲染周期进行数据共享,因为每次保存都会刷新数据重新渲染

示例程序:(其中的timer无法跨渲染周期,每次执行App函数,其都会被重置)

import React, { useState, useEffect, useRef } from "react";
function App() {
const [count, setCount] = useState(0);
// 把定时器设置成全局变量使用useRef挂载到current上
const timer = useRef();
// 首次加载useEffect方法执行一次设置定时器
useEffect(() => {
timer.current = setInterval(() => {
setCount(count => count + 1); // 此处必须传入函数,否则拿到的都是组将挂载时的初值!!
}, 1000);
}, []);
// count每次更新都会执行这个副作用,当count > 5时,清除定时器
useEffect(() => {
if (count > 5) {
clearInterval(timer.current);
}
});
return <h1>count: {count}</h1>;
}
export default App;

6 各方式使用总结

ref方式定义获取元素引用执行挂载在元素内的函数备注
string ref标签内ref=‘xxx’this.refs.xxxthis.refs.xxx.handle()已过时
callback ref标签内ref={(element)=>{this.xxx=element}}this.xxxthis.xxx.handle()精准控制ref
create ref构造中this.xxx=createRef() 标签中ref={this.xxx}this.xxx.currentthis.xxx.current.handle()推荐使用
useRef()函数组件中const 函数标签中xxx=useRef() ref={xxx}xxx.currentxxx.current.handle()函数组件中的ref替代方法

参考文档

7 Ref转发

有时候需要传递ref给子组件,需要用到React.forwardRef函数,因为常规函数组件和 class 组件不接收 ref 参数,且 props 中也不存在 ref

const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// 你可以直接获取 DOM button 的 ref:
const ref = React.createRef();
// 此时的ref即React.forwardRef的参数之一
<FancyButton ref={ref}>Click me!</FancyButton>;

这个例子展示了,将ref转发到了最终的button标签上,其实Ref 转发不仅限于 DOM 组件,你也可以转发 refs到 class组件实例中。

HOC Ref转发

function logProps(Component) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
const {forwardedRef, ...rest} = this.props;
// 将自定义的 prop 属性 “forwardedRef” 定义为 ref
return <Component ref={forwardedRef} {...rest} />;
}
}
// 注意 React.forwardRef 回调的第二个参数 “ref”。
// 我们可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
// 然后它就可以被挂载到被 LogProps 包裹的子组件上。
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
}

这时候logProps如何调用,怎么同时把Component和ref传进去
猜测:

const Test = logProps(<button>123</button>)
const ref = React.createRef()
<Test ref={ref}/>
posted @   Tommy_marc  阅读(394)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体
点击右上角即可分享
微信分享提示