This post is part of a series on Horseshoe React, with more to come

Come to my GitHub repo to read the full feature article

Come to my personal blog for an unparalleled reading experience

React exists to separate the state from the UI, so that developers don’t know there is a DOM, whether wei or Jin.

However, some states cannot be separated from the UI, such as the focus of a form, complex animations, and so on.

How to do? Until React took full control of the DOM, it still gave developers a back door.

this.refs

💀 This is an API that React is no longer recommended.

Each class component instantiates a refs property, which is used to store DOM references.

By passing a ref attribute with a string value to the DOM element, the developer gets a reference to the DOM element, which can be found under the this.refs object.

Since you’re retrieving a reference to a DOM element, you can’t operate on it until the component has been mounted.

React doesn’t recommend this anymore. Mainly because it’s a bit of a performance drag, because the UI goes through a lot of updates, and string references don’t automatically track DOM changes, React does some extra processing.

We may not see the refs attribute of an instance in a future version.

import React, { Component } from 'react';

class App extends Component {
    componentDidMount() {
        this.refs.textInput.focus();
    }
    
    render() {
        return (
            <input type="text" ref="textInput" />
        );
    }
}

export default App;
Copy the code

callback

React also supports receiving references to DOM elements with a callback.

But remember, the callback should not be el => this.refs.textinput = el, because this.refs cannot write directly.

import React, { Component } from 'react';

class App extends Component {
    componentDidMount() {
        this.textInput.focus();
    }
    
    render() {
        return (
            <input type="text" ref={el= > this.textInput = el} />
        );
    }
}

export default App;
Copy the code

Of course, a callback can do a lot of things. For example, it can walk through walls.

The same callback is used to receive references to DOM elements, but this time the callback is passed down by the parent component via props.

Once the child component is mounted, the REF callback is executed and the parent component gets a reference to a DOM element of the child component.

import React, { Component } from 'react';
import Search from './Search';

class App extends Component {
    getInputRef = (ref) = > {
        this.node = ref;
    }
    
    render() {
        return (
            <Search ref={this.getInputRef} />
        );
    }
}

export default App;
Copy the code
import React from 'react';

const Search = (props) = > (
    <input type="text" ref={props.getInputRef} />
);

export default Search;
Copy the code

createRef

👽 This is the React V16.3.0 release API.

CreateRef creates a REF object.

The createRef execution results are returned to an instance attribute, which is then used to obtain a reference to the DOM element.

Matters needing attention:

  • createRefThe initialization action is performed before the component is mounted; if it is initialized after the mount, no reference to the DOM element can be obtained.
  • The actual DOM element is referenced on the current attribute.
import React, { Component, createRef } from 'react';

class App extends Component {
    textInput = createRef();

    componentDidMount() {
        this.textInput.current.focus();
    }

    render() {
        return (
            <input type="text" ref={this.textInput} />
        );
    }
}

export default App;
Copy the code

For nondescribable reasons, if you want to get a ref reference to a child component, the child component must be a class component.

Because what you’re actually getting is an instance of a child component, whereas functional components don’t have an instance.

Any way to get a ref reference, if you want to get a subcomponent instead of a DOM element, the subcomponent cannot be a functional component.

import React, { Component, createRef } from 'react';
import Child from './Child';

class App extends Component {
    childRef = createRef();

    render() {
        return (
            <Child ref={this.childRef} />
        );
    }
}

export default App;
Copy the code

forwardRef

👽 This is the React V16.3.0 release API.

You can use callbacks to get references to child components’ DOM elements, but this technique is still a hack.

So the thoughtful React provides us with a martial art that cuts through walls.

There’s nothing special about the parent component, except this time createRef doesn’t return a result to one of its OWN DOM elements, it returns a child component.

The key is the subcomponent, which passes itself as a parameter to the forwardRef. The subcomponent then gets the ref parameter, just outside of the props parameter, and assigns the ref parameter to the REF attribute of a DOM element.

Do you see that? The forwardRef acts as a forwarder and is actually a container component.

ForwardRef. That’s why it’s called the forwardRef.

Note that when using the forwardRef, the component must be functional. The reason may be that React doesn’t want to break the class component’s argument architecture.

We can’t use functional components to get a ref reference from a component.

If you look closely, there’s a fundamental difference. You’re still getting DOM elements, but you’re getting them across levels.

import React, { Component, createRef } from 'react';
import Search from './Search';

class App extends Component {
    textInput = createRef();

    componentDidMount() {
        this.textInput.current.focus();
    }

    render() {
        return (
            <Search ref={this.textInput} />
        );
    }
}

export default App;
Copy the code
import React, { forwardRef } from 'react';

const Search = forwardRef((props, ref) = > (
    <input type="text" ref={ref} />
));

export default Search;
Copy the code

Now that we’re stepping up, can we make it bigger?

B: Sure.

In fact, once the subcomponent wrapped by the forwardRef receives the ref argument, it can continue passing the ref down. Through what? Props, of course!

Ref then becomes just props, and you want to do whatever you want until it’s mounted on the REF attribute of a DOM element.

There is no distinction between class components and functional components, because both class components and functional components can accept props. Its job is simply to mount this particular prop to a particular DOM element for an unknown number of generations of ancestors.

Ref callbacks can also be passed across multiple levels.

import React, { Component, createRef } from 'react';
import Search from './Search';

class App extends Component {
    textInput = createRef();

    render() {
        return (
            <Search ref={this.textInput} />
        );
    }
}

export default App;
Copy the code
import React from 'react';
import Input from './Input';

const Search = forwardRef((props, ref) = > (
    <Input inputRef={ref} />
));

export default Search;
Copy the code
import React, { Component } from 'react';

class Input extends Component {
    render() {
        return (
            <input type="text" ref={this.props.inputRef} />
        );
    }
}

export default Input;
Copy the code

React

What is the UI

JSX

State variable

Immutable property

The life cycle

component

The event

Operating the DOM

Abstract UI