Refs提供了一种允许我们访问在渲染方法中创建的DOM节点或React元素的方法。Refs使用场景在某些情况下,我们需要强制修改典型数据流之外的子组件。修改后的子组件可能是React组件或DOM元素的实例,例如:管理焦点、文本选择或媒体播放。触发强制动画。集成第三方DOM库。设置Refs1。createRef支持在函数组件和类组件内部使用createRef,React16.3版本引入。创建Refs使用React.createRef()创建Refs并通过ref属性将它们附加到React元素。通常在构造函数中,Refs被分配给要在整个组件中引用的实例属性。访问引用当引用传递给渲染中的元素时,可以在引用的当前属性中访问对节点的引用。importReactfrom'react';exportdefaultclassMyInputextendsReact.Component{constructor(props){super(props);//赋给实例属性this.inputRef=React.createRef(null);}componentDidMount(){//通过this.inputRef获取。current对该节点的引用this.inputRef&&this.inputRef.current.focus();}render(){//关联ref到构造函数中创建的`inputRef`return()}}ref因节点类型而异:当ref属性用于HTML元素时,在构造函数中使用React.createRef()创建的ref接收底层DOM元素作为其当前元素属性。当ref属性用于自定义类组件时,ref对象接收组件的已安装实例作为其当前属性。ref属性不能用在函数组件上,因为函数组件没有实例。总结:给DOM添加ref,那么我们就可以通过ref获取到DOM节点的引用。并且给React组件加上ref,那么我们就可以通过ref来获取组件的实例【函数组件不能使用ref属性,因为函数组件没有实例】。2.useRef仅限在函数组件中使用useRef是React16.8引入的,只能在函数组件中使用。创建Refs使用React.useRef()创建Refs并通过ref属性将它们附加到React元素。constrefContainer=useRef(初始值);useRef返回的ref对象在组件的整个生命周期中保持不变。访问引用当引用传递给React元素时,可以在引用的当前属性中访问对节点的引用。importReactfrom'react';exportdefaultfunctionMyInput(props){constinputRef=React.useRef(null);React.useEffect(()=>{inputRef.current.focus();});return()}关于React.useRef()返回的ref对象在组件的整个生命周期中保持不变。让我们将它与React.createRef()进行比较。代码如下:importReact,{useRef,useEffect,createRef,useState}from'react';functionMyInput(){let[count,setCount]=useState(0);constmyRef=createRef(null);constinputRef=useRef(null);//只执行一次useEffect(()=>{inputRef.current.focus();window.myRef=myRef;window.inputRef=inputRef;},[]);useEffect(()=>{//except第一次为真,其他时候为假【createRef】console.log('myRef===window.myRef',myRef===window.myRef);//总是为真【useRef】console.log('inputRef===window.inputRef',inputRef===window.inputRef);})return(<>setCount(count+1)}>{count}>)}3.CallbackRefs支持以React支持函数组件和类组件内部回调refs的方式设置Refs。这种方法可以帮助我们更好地控制何时设置和取消设置Refs。使用回调refs需要将回调函数传递给React元素的ref属性。该函数接受一个React组件实例或HTMLDOM元素作为参数,并将其挂载到实例属性上,如下所示:this.setTextInputRef=(ele)=>{this.inputRef=ele;}}componentDidMount(){this.inputRef&&this.inputRef.focus();}render(){return()}}React会在组件挂载时调用ref回调函数并传入DOM元素(或React实例),组件卸载时调用并传入null。在componentDidMoune或componentDidUpdate被触发之前,React将确保Refs必须是最新的。refs.importReactfrom'react';exportdefaultfunctionForm(){letref=null;React.useEffect(()=>{//ref是MyInput中ref.focus();}中的输入节点,可以在组件之间传递回调形式[ref]);return(<>ref=ele}/>{/**othercode*/>>)}functionMyInput(props){return()}4。StringRefs(ObsoleteAPI)函数组件不支持使用字符串refs[支持createRef|使用参考|callbackRef]functionMyInput(){return(<>)}类组件通过this.refs.XXX获取React元素。classMyInputtextendsReact.Component{componentDidMount(){this.refs.inputRef.focus();}render(){return()}}Ref在Hook之前传递,highHoccomponents(HOC)和renderprops是React中重用组件逻辑的主要方式。虽然高阶组件的约定是将所有的props传递给被包裹的组件,但refs是不会传递的。事实上,ref不是prop。和key一样,它由React专门处理。这个问题可以通过React.forwardRef(React16.3中的新功能)来解决。在React.forwardRef之前,针对这个问题,我们可以在容器组件中添加forwardedRef(prop的名字可以自己定,但是不能是ref或者key)。React.forwardRefimportReactfrom'react';importhoistNonReactStaticfrom'hoist-non-react-statics';constwithData=(WrappedComponent)=>{classProxyComponenttextendsReact.Component{componentDidMount(){//code}//这里有一点需要注意的是在使用的时候,我们需要知道这个组件是一个wrapped组件//设置ref值..remainingProps}/>)}}//指定显示名称。staticmethodnotcopyed(重点是不讲HOC)ProxyComponent.displayName=WrappedComponent.displayName||WrappedComponent.name||'Component';//复制非React静态方法hoistNonReactStatic(ProxyComponent,WrappedComponent);returnProxyComponent;}在这个例子中,我们将ref属性值通过forwardedRef的prop传递给被包裹的组件,使用:classMyInputtendsReact.Component{render(){return()}}MyInput=withData(MyInput);functionForm(props){constinputRef=React.useRef(null);React.useEffect(()=>{console.log(inputRef.current)})//我们在使用MyInput的时候,需要区分是不是封装组件来判断是指定ref还是forwardedRefreturn()}React.forwardRefRef转发它是一种通过组件自动将ref传递给其子组件之一的技术,它允许某些组件接收ref并将其向下传递给子组件以将ref转发给DOM:importReactfrom'react';constMyInput=React。forwardRef((props,ref)=>{return()});functionForm(){constinputRef=React.useRef(null);反应。useEffect(()=>{console.log(inputRef.current);//输入节点})return()}调用React.useRef创建一个Reactref并将其赋值给ref变量.将ref指定为JSX属性并向下传递React将ref传递给forwardRef内部函数(props,ref)=>...作为其第二个参数。将ref参数向下转发到,并将其指定为JSX属性。当ref挂载完成后,inputRef.current指向输入DOM节点。注意,第二个参数ref仅在使用React.forwardRef定义组件存在时使用。常规函数和类组件不接受ref参数,props中不存在ref。在React.forwardRef之前,如果我们要给子组件传递ref属性,需要区分是否是HOC封装的组件,使用起来会带来一些不便。让我们使用React.forwardRef进行重构。importReactfrom'react';importhoistNonReactStaticfrom'hoist-non-react-statics';functionwithData(WrappedComponent){classProxyComponenttextendsReact.Component{componentDidMount(){//code}render(){const{forwardedRef,...remainingProps}=this.props;return()}}//当我们使用wrappedwithData的组件时,只需要传递refconstforwardRef=React.forwardRef((props,ref)=>());//指定displayName.forwardRef.displayName=WrappedComponent.displayName||WrappedComponent.name||'Component';returnhoistNonReactStatic(forwardRef,WrappedComponent);}classMyInputtextendsReact.Component{render(){return()}}MyInput.getName=function(){console.log('name');}MyInput=withData(MyInput);console.log(MyInput.getName);//测试静态方法拷贝是否正常functionForm(props){constinputRef=React.useRef(null);React.useEffect(()=>{console.log(inputRef.current);//包装组件MyInput})//在使用时,传ref给return()}react-redux得到子组件的实例(wrappedpuppetcomponent)在老版本(V4/V5)我们知道connect有四个参数,如果我们想要父组件中的子组件(puppet组件)的实例,那么我们需要将第四个参数options的withRef设置为true然后就可以通过父组件中容器组件实例的getWrappedInstance()方法获取puppet组件(wrapped组件)的实例,如下://MyInput.jsimportReactfrom'react';import{connect}from'react-redux';classMyInputextendsReact.Component{render(){return()}}exportdefaultconnect(null,null,null,{withRef:true})(MyInput);//index.jsimportReactfrom"反应";importReactDOMfrom"react-dom";import{createStore}from'redux';import{Provider}from'react-redux';importMyInputfrom'./MyInput';functionreducer(state,action){returnstate;}conststore=createStore(reducer);functionMain(){letref=React.createRef();React.useEffect(()=>{console.log(ref.current.getWrappedInstance());})return()}ReactDOM.render(,document.getElementById("root"));这里需要注意的是:MyInput必须是class组件,而function组件没有实例,自然无法通过ref获取其实例。在react-redux源码中,通过给wrapped组件添加ref属性,getWrappedInstance返回实例this.refs.wrappedInstance。if(withRef){this.renderedElement=createElement(WrappedComponent,{...this.mergedProps,ref:'wrappedInstance'})}新版(V6/V7)新版react-redux使用了React.forwardRef方法参考转发。从V6版本开始,withRefinoption已经被废弃。如果要获取打包组件的实例,需要指定connect的第四个参数选项的forwardRef为true。详情见以下示例://MyInput.js文件importReactfrom'react';import{connect}from'react-redux';classMyInputtextendsReact.Component{render(){return()}}exportdefaultconnect(null,null,null,{forwardRef:true})(MyInput);直接在被包裹组件中添加ref,即可以获取被包裹组件的实例,如下图://index.jsimportReactfrom"react";importReactDOMfrom"react-dom";import{createStore}from'redux';import{Provider}from'react-redux';importMyInputfrom'./MyInput';functionreducer(state,action){returnstate;}conststore=createStore(reducer);functionMain(){letref=React.createRef();React.useEffect(()=>{console.log(ref.current);})return()}ReactDOM.render(,document.getElementById("root"));同样,MyInput必须是类组件,因为函数组件没有实例,自然无法通过ref获取其实例。在react-redux中,ref被转发到Connect组件。通过forwardedRef传递给被包装组件WrappedComponent的ref。if(forwardRef){constforwarded=React.forwardRef(functionforwardConnectRef(props,ref){return})forwarded.displayName=displayNameforwarded.WrappedComponent=WrappedComponentreturnhoistStatics(forwarded,WrappedComponent)}//...const{forwardedRef,...wrapperProps}=propsconstrenderedWrappedComponent=useMemo(()=>,[forwardedRef,WrappedComponent,actualChildProps])