一次性搞定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.xxx | this.refs.xxx.handle() | 已过时 |
callback ref | 标签内ref={(element)=>{this.xxx=element}} | this.xxx | this.xxx.handle() | 精准控制ref |
create ref | 构造中this.xxx=createRef() 标签中ref={this.xxx} | this.xxx.current | this.xxx.current.handle() | 推荐使用 |
useRef() | 函数组件中const 函数标签中xxx=useRef() ref={xxx} | xxx.current | xxx.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}/>
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体