All you want to know about Refs is here.

  Front end, html5, javascript, react.js, redux

RefsProvides a way to access DOM nodes orrenderThe React element created in the method.

Refs usage scenario

In some cases, we need to force the modification of a sub-component outside the typical data stream. The modified sub-component may be an instance of a React component or a DOM element, for example:

  • Manage focus, text selection or media playback.
  • Triggers forced animation.
  • Integrate third-party DOM libraries.

Set Refs

1. createRef

Supports use within function components and class components

createRefIt was introduced in React16.3 version 16.3.

Create Refs

UseReact.createRef()CreateRefsAnd passedrefAttribute attached toReactElement. Usually in the constructor, theRefsAssigned to instance properties for reference throughout the component.

Access Refs

WhenrefDelivered torenderA reference to this node can be found in therefThecurrentProperty.

import React from 'react';
export default class MyInput extends React.Component {
    constructor(props) {
        super(props);
        //分配给实例属性
        this.inputRef = React.createRef(null);
    }

    componentDidMount() {
        //通过 this.inputRef.current 获取对该节点的引用
        this.inputRef && this.inputRef.current.focus();
    }

    render() {
        //把 <input> ref 关联到构造函数中创建的 `inputRef` 上
        return (
            <input type="text" ref={this.inputRef}/>
        )
    }
}

refThe value of varies depending on the type of node:

  • WhenrefThe property is used forHTMLElement, theReact.createRef()CreatedrefReceiving floorDOMElement as itscurrentProperty.
  • WhenrefWhen the property is used for a custom class component,refObject receives the mounted instance of the component as itscurrentProperty.
  • Cannot be used on function componentsrefProperty because the function component does not have an instance.

Summary: add to DOMref, then we can passrefGets a reference to the DOM node. Add to React componentref, then we can passrefAn instance of this component was obtained [ref attribute cannot be used on function component because function component has no instance].

2. useRef

Only used within function components

useRefIt is introduced in React16.8 and can only be used in function components.

Create Refs

UseReact.useRef()CreateRefsAnd passedrefAttribute attached toReactElement.

const refContainer = useRef(initialValue);

useRefThe ref object returned is in the component’sRemain the same throughout the life cycle.

Access Refs

WhenrefWhen passed to the React element, a reference to that node can be found in therefThecurrentProperty.

import React from 'react';

export default function MyInput(props) {
    const inputRef = React.useRef(null);
    React.useEffect(() => {
        inputRef.current.focus();
    });
    return (
        <input type="text" ref={inputRef} />
    )
}

AboutReact.useRef()The ref object returned remains unchanged throughout the life cycle of the component. Let’s sum upReact.createRef()To make a comparison, the code is as follows:

import React, { useRef, useEffect, createRef, useState } from 'react';
function MyInput() {
    let [count, setCount] = useState(0);

    const myRef = createRef(null);
    const inputRef = useRef(null);
    //仅执行一次
    useEffect(() => {
        inputRef.current.focus();
        window.myRef = myRef;
        window.inputRef = inputRef;
    }, []);
    
    useEffect(() => {
        //除了第一次为true, 其它每次都是 false 【createRef】
        console.log('myRef === window.myRef', myRef === window.myRef);
        //始终为true 【useRef】
        console.log('inputRef === window.inputRef', inputRef === window.inputRef);
    })
    return (
        <>
            <input type="text" ref={inputRef}/>
            <button onClick={() => setCount(count+1)}>{count}</button>
        </>
    )
}

3. callback Refs

Supports use within function components and class components

ReactSupportRefs callbackTo set Refs. This method can help us to control more finely when Refs is set and released.

UseRefs callbackYou need to pass the callback function toReact elementTherefProperty. This function takes the React component instance or HTML DOM element as a parameter and mounts it on the instance attribute as follows:

import React from 'react';

export default class MyInput extends React.Component {
    constructor(props) {
        super(props);
        this.inputRef = null;
        this.setTextInputRef = (ele) => {
            this.inputRef = ele;
        }
    }

    componentDidMount() {
        this.inputRef && this.inputRef.focus();
    }
    render() {
        return (
            <input type="text" ref={this.setTextInputRef}/>
        )
    }
}

React calls when the component is mountedrefCallback function and pass in DOM element (or React instance), call it when uninstalling and pass innull. IncomponentDidMountOrcomponentDidUpdateBefore triggering, React will ensure that Refs must be up-to-date.

Refs in the form of callbacks can be passed between components.

import React from 'react';

export default function Form() {
    let ref = null;
    React.useEffect(() => {
        //ref 即是 MyInput 中的 input 节点
        ref.focus();
    }, [ref]);

    return (
        <>
            <MyInput inputRef={ele => ref = ele} />
            {/** other code */}

        </>
    )
}

function MyInput (props) {
    return (
        <input type="text" ref={props.inputRef}/>
    )
}

4. String Refs (Obsolete API)

The use of the string refs [createRef | useRef | Callback Ref] is not supported inside a function component

function MyInput() {
    return (
        <>
            <input type='text' ref={'inputRef'} />
        </>
    )
}

16dfbb1de78766f8.jpg

Class component

viathis.refs.XXXGets the React element.

class MyInput extends React.Component {
    componentDidMount() {
        this.refs.inputRef.focus();
    }
    render() {
        return (
            <input type='text' ref={'inputRef'} />
        )
    }
}

Ref transfer

Prior to Hook, high-level components (HOC) and rendererspropsIt is the main means of reusing component logic in React.

Although the agreement of high-level components will allpropsPassed to the packaged component, butrefsWill not be passed on, in fact,refNot oneprop, andkeySimilarly, it is specially handled by React.

This problem can be solved throughReact.forwardRef(added in React 16.3) to solve. InReact.forwardRefPreviously, this problem could be solved by addingforwardedRef(prop’s name is determined by itself, but it cannot be ref or key).

Before React.forwardRef

import React from 'react';
import hoistNonReactStatic from 'hoist-non-react-statics';

const withData = (WrappedComponent) => {
    class ProxyComponent extends React.Component {
        componentDidMount() {
            //code
        }
        //这里有个注意点就是使用时,我们需要知道这个组件是被包装之后的组件
        //将ref值传递给 forwardedRef 的 prop
        render() {
            const {forwardedRef, ...remainingProps} = this.props;
            return (
                <WrappedComponent ref={forwardedRef} {...remainingProps}/>
            )
        }
    }
    //指定 displayName.   未复制静态方法(重点不是为了讲 HOC)
    ProxyComponent.displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
    //复制非 React 静态方法
    hoistNonReactStatic(ProxyComponent, WrappedComponent);
    return ProxyComponent;
}

In this example, we willrefThe property value of the passedforwardedRefTheprop, passed to the packaged component, using:

class MyInput extends React.Component {
    render() {
        return (
            <input type="text" {...this.props} />
        )
    }
}

MyInput = withData(MyInput);
function Form(props) {
    const inputRef = React.useRef(null);
    React.useEffect(() => {
        console.log(inputRef.current)
    })
    //我们在使用 MyInput 时,需要区分其是否是包装过的组件,以确定是指定 ref 还是 forwardedRef
    return (
        <MyInput forwardedRef={inputRef} />
    )
}

React.forwardRef

Ref forwarding is a technique for automatically passing ref through components to one of its subcomponents, which allows some components to receive ref and pass it down to the subcomponents.

Forward ref to DOM:

import React from 'react';

const MyInput = React.forwardRef((props, ref) => {
    return (
        <input type="text" ref={ref} {...props} />
    )
});
function Form() {
    const inputRef = React.useRef(null);
    React.useEffect(() => {
        console.log(inputRef.current);//input节点
    })
    return (
        <MyInput ref={inputRef} />
    )
}
  1. callReact.useRefCreated aReact refAnd assign it torefVariable.
  2. appointrefIs a JSX attribute and is passed down<MyInput ref={inputRef}>
  3. React transferreftoforwardRefInternal function(props, ref) => ...As its second parameter.
  4. Forward this downrefParameters to<button ref={ref}>To specify it as a JSX attribute
  5. WhenrefMount is complete.inputRef.currentpoint toinputDOM node

pay attention to

Second parameterrefOnly in useReact.forwardRefExists when defining a component. Regular functions and class components do not receiverefParameters, andpropsNor does it exist inref.

InReact.forwardRefBefore, if we want to passrefAttribute to subcomponents, it is necessary to distinguish whether it is a component packaged by HOC, which causes some inconvenience to use. Let’s use itReact.forwardRefReconstruction.

import React from 'react';
import hoistNonReactStatic from 'hoist-non-react-statics';

function withData(WrappedComponent) {
    class ProxyComponent extends React.Component {
        componentDidMount() {
            //code
        }
        render() {
            const {forwardedRef, ...remainingProps} = this.props;
            return (
                <WrappedComponent ref={forwardedRef} {...remainingProps}/>
            )
        }
    }
    
    //我们在使用被withData包装过的组件时,只需要传 ref 即可
    const forwardRef = React.forwardRef((props, ref) => (
        <ProxyComponent {...props} forwardedRef={ref} />
    ));
    //指定 displayName.
    forwardRef.displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
    return hoistNonReactStatic(forwardRef, WrappedComponent);
}
class MyInput extends React.Component {
    render() {
        return (
            <input type="text" {...this.props} />
        )
    }
}
MyInput.getName = function() {
    console.log('name');
}
MyInput = withData(MyInput);
console.log(MyInput.getName); //测试静态方法拷贝是否正常


function Form(props) {
    const inputRef = React.useRef(null);
    React.useEffect(() => {
        console.log(inputRef.current);//被包装组件MyInput
    })
    //在使用时,传递 ref 即可
    return (
        <MyInput ref={inputRef} />
    )
}

Get instances of subcomponents (wrapped puppet components) in react-redux

In the old version (V4/V5)

We know that,connectThere are four parameters. If we want to have instances of child components (puppet components) in the parent component, then we need to set the fourth parameter.optionsThewithRefFortrue. You can then pass through the of the container component instance in the parent componentgetWrappedInstance()The method gets an instance of the puppet component (the packaged component), as follows:

//MyInput.js
import React from 'react';
import { connect } from 'react-redux';

class MyInput extends React.Component {
    render() {
        return (
            <input type="text" />
        )
    }
}
export default connect(null, null, null, { withRef: true })(MyInput);
//index.js
import React from "react";
import ReactDOM from "react-dom";
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import MyInput from './MyInput';

function reducer(state, action) {
    return state;
}
const store = createStore(reducer);

function Main() {
    let ref = React.createRef();
    React.useEffect(() => {
        console.log(ref.current.getWrappedInstance());
    })
    return (
        <Provider store={store}>
            <MyInput ref={ref} />
        </Provider>
    )
}

ReactDOM.render(<Main />, document.getElementById("root"));

What needs attention here is:MyInputIt must be a class component, and the function component has no instance and cannot pass through naturally.refGets its instance.react-reduxIn the source code, by addingrefAttributes,getWrappedInstanceThis instance is returnedthis.refs.wrappedInstance.

if (withRef) {
    this.renderedElement = createElement(WrappedComponent, {
        ...this.mergedProps,
        ref: 'wrappedInstance'
    })
}

New Version (V6/V7)

react-reduxIt is used in the new versionReact.forwardRefThe method was carried outrefForward. Since the V6 version,optionhit the targetwithRefAbandoned, if you want to get an instance of the packaged component, you need to specifyconnectThe fourth parameter of theoptionTheforwardRefFortrue, see the following example:

//MyInput.js 文件
import React from 'react';
import { connect } from 'react-redux';

class MyInput extends React.Component {
    render() {
        return (
            <input type="text" />
        )
    }
}
export default connect(null, null, null, { forwardRef: true })(MyInput);

Adding directly to the packaged componentsref, that is, an instance of the packaged component can be obtained as follows:

//index.js
import React from "react";
import ReactDOM from "react-dom";
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import MyInput from './MyInput';

function reducer(state, action) {
    return state;
}
const store = createStore(reducer);

function Main() {
    let ref = React.createRef();
    React.useEffect(() => {
        console.log(ref.current);
    })
    return (
        <Provider store={store}>
            <MyInput ref={ref} />
        </Provider>
    )
}


ReactDOM.render(<Main />, document.getElementById("root"));

Similarly,MyInputMust be a class component, because the function component has no instance and cannot pass through naturally.refGets its instance.

Lieutenant general react-reduxrefForward toConnectIn the component. viaforwardedRefDelivered to Packaged ComponentsWrappedComponentTheref.

if (forwardRef) {
    const forwarded = React.forwardRef(function forwardConnectRef(
        props,
        ref
    ) {
        return <Connect {...props} forwardedRef={ref} />
    })

    forwarded.displayName = displayName
    forwarded.WrappedComponent = WrappedComponent
    return hoistStatics(forwarded, WrappedComponent)
}

//...
const { forwardedRef, ...wrapperProps } = props
const renderedWrappedComponent = useMemo(
    () => <WrappedComponent {...actualChildProps} ref={forwardedRef} />,
    [forwardedRef, WrappedComponent, actualChildProps]
)

Reference link: