当前位置: 首页 > Web前端 > JavaScript

React中Refs的使用及forwardRef的源码解读

时间:2023-03-27 01:40:02 JavaScript

React提供了Refs的三种使用方法来帮助我们访问在render方法中创建的DOM节点或React元素。React提供了三种使用Ref的方式:1.StringRefsclassAppextendsReact.Component{constructor(props){super(props)}componentDidMount(){setTimeout(()=>{//2.通过this.refs.xxx获取DOM节点this.refs.textInput.value='newvalue'},2000)}render(){//1.ref直接传入一个字符串return(

)}}root.render();2.callbackRefsclassAppextendsReact.Component{constructor(props){super(props)}componentDidMount(){setTimeout(()=>{//2.通过实例属性获取DOM节点this.textInput.value='newvalue'},2000)}render(){//1.ref传入一个回调函数//这个函数接受ReactcomponentsInstance或DOM元素作为参数//我们通常将其存储在一个具体的实例属性(this.textInput)中return(
<输入ref={(元素)=>{this.textInput=元素;}}value='value'/>
)}}root.render();3.createRefclassAppextendsReact.Component{constructor(props){super(props)//1.使用createRef创建Refs//并将Refs分配给实例属性textInputRef以引用this.textInputRef=React.createRef();}componentDidMount(){setTimeout(()=>{//3.通过Refs的当前属性引用this.textInputRef.current.value='newvalue'},2000)}render(){//2.附加到React元素通过ref属性返回(
)}}这是最推荐的方式。两个使用目的。Refs不仅用来获取具体的DOM节点,还可以用来获取Class组件的Instance,当获取到实例后,可以调用该方法强制执行,比如动画等效果。让我们以获取组件实例为例:}handleFocus(){this.textInputRef.current.focus();}render(){return}}classAppextendsReact.Component{constructor(props){super(props)this.inputRef=React.createRef();}componentDidMount(){setTimeout(()=>{this.inputRef.current.handleFocus()},2000)}render(){return(
)}}本例中,我们通过this.inputRef.current获取Input组件的实例,并调用该实例的handleFocus方法。在这个方法中,我们通过Refs获取到具体的DOM元素,执行焦点native方法。forwardRef注意,在这个例子中,我们的Input组件使用的是类组件,那么Input组件是否可以改用函数组件呢?答案是否定的,我们不能在函数组件上使用ref属性,因为函数组件没有实例。如果我们强制执行,React会报错并提示我们使用forwardRef:.inputRef=React.createRef();}render(){return(
)}}然而,对于“获取组件实例,调用实例方法”的需求,即使使用forwardRef,我们做不到。有了forwardRef,我们就可以像类组件一样使用组件上的ref属性,然后将ref绑定到具体的DOM元素或类组件上,也就是我们常说的Refs转发。Refs转发有时候,我们在开发一个组件的时候,这个组件需要提供一个ref属性给组件使用者,让组件使用者获取特定的DOM元素,我们需要进行Refs转发,我们通常的做法是://类组件classChildextendsReact.Component{render(){const{inputRef,...rest}=this.props;//3.这里将props中的inputRef赋值给DOM元素的ref{const{inputRef,...rest}=props;//3.这里将props中的inputRef赋值给DOM元素的ref(props)//1.创建refsthis。inputRef=React.createRef();}componentDidMount(){setTimeout(()=>{//4.使用this.inputRef.current获取子组件渲染的DOM节点this.inputRef.current.value='newvalue'},2000)}render(){//2.因为无法通过this.props获取ref属性,所以这里是不同的属性名return<ChildinputRef={this.inputRef}/>}}React提供了forwardRefAPI,我们直接看用法示例://3.子组件通过forwardRef获取ref,通过ref属性const绑定React元素Child=forwardRef((props,ref)=>());classParentextendsReact.Component{constructor(props){super(props)//1.创建引用this.inputRef=React.createRef();}componentDidMount(){setTimeout(()=>{//4.使用this.inputRef.current获取子组件渲染的DOM节点this.inputRef.current.value='newvalue'},2000)}render(){//2.传递给子组件的ref属性return}}尤其是我们在写高层组件的时候,经常需要实现refs来转发给我们众所周知即高层组件会接受一个组件并返回一个包装好的新组件,从而实现某种功能增强。但也确实如此,当我们添加ref时,我们会得到被包装的新组件的实例,而不是被包装的组件实例,这可能会导致一些问题。createRef源码下面我们来看一下createRef的源码,源码位于/packages/react/src/ReactCreateRef.js,代码其实很简单,只是返回一个具有当前属性的对象://SimplifiedexportfunctioncreateRef(){constrefObject={current:null,};returnrefObject;}在渲染过程中,refObject.current会被赋予一个特定的值。forwardRef源码,forwardRef源码呢?源码位置在/packages/react/src/ReactForwardRef.js,代码很简单://SimplifiedconstREACT_FORWARD_REF_TYPE=Symbol.for('react.forward_ref');exportfunctionforwardRef(render){constelementType={$$typeof:REACT_FORWARD_REF_TYPE,render,};returnelementType;}但是要注意这里的$$typeof,虽然是REACT_FORWARD_REF_TYPE,但是最终创建的React元素的$$typeof还是REACT_ELEMENT_TYPE。createElement的源码分析可以参考《React 之 createElement 源码解读》。这里简单分析一下,以InputComponent为例://使用forwardRefconstInputComponent=forwardRef(({value},ref)=>()));//根据forwardRef的源码,最终返回的对象格式为:constInputComponent={$$typeof:REACT_FORWARD_REF_TYPE,render,}//使用组件constresult=//Bable翻译成:constresult=React.createElement(InputComponent,null);//最终返回的对象是:constresult={$$typeof:REACT_ELEMENT_TYPE,type:{$$typeof:REACT_FORWARD_REF_TYPE,render,}}我们尝试打印最终的返回对象,确实是同一个结构:React系列讲解React源码,ReactAPI背后的实现机制,React最佳实践,React的发展与历史等,估计有50篇左右,喜欢的话欢迎关注或者有什么灵感,欢迎star,就是也是对作者的鼓励。

最新推荐
猜你喜欢