In my last blog post, I outlined some of the core concepts in the React documentation.

Continue reading advanced guidelines from this blog post, which focuses on Ref in React.

The role of the Ref

Refs provides a way to access DOM nodes or React elements created in the Render method.

In a typical React data flow, the props is the only way for the parent component to interact with its children. To modify a child component, you need to re-render it using new props. However, in some cases, you need to force changes to subcomponents outside of the typical data flow. The child component being modified may be an instance of the React component or a DOM element. React offers solutions in both cases.

The Ref provides a new way for you to modify a custom component or DOM directly.

When to use Refs

Here are a few situations where refs are appropriate:

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

Avoid using Refs for anything that can be done with a declarative implementation.

For example, avoid exposing the open() and close() methods in the Dialog component and pass the isOpen property instead.

What is declarative

The above paragraph mentioned declarative, what is declarative?

In a nutshell, it’s a programming paradigm, and it’s the same concept as imperative to functional programming, object-oriented programming.

Declarative means to state the purpose without specifying how to do it.

As in this example in the documentation, open() is a declarative programming paradigm that states the purpose without specifying how to do it. If isOpen=true, it is imperative, it tells you what to do, and the way to do it is to set isOpen to true.

Specific can go to have a look at: segmentfault.com/a/119000001…

Don’t overuse Refs

The first thing you might think of is using Refs to “make things happen” in your app. If this is the case, take a moment and seriously reconsider which component layer the state property should be placed in. In general, you might think it would be more appropriate to have this state at a higher component level. See status promotions for more examples.

How to use Ref

Create the Refs

Refs are created using react.createref () and attached to the React element via the ref attribute. When components are constructed, Refs are typically assigned to instance properties so that they can be referenced throughout the component.

class MyComponent extends React.Component { constructor(props) { super(props); this.myRef = React.createRef(); } render() { return <div ref={this.myRef} />; }}Copy the code

Access to the Refs

When a ref is passed to an element in render, a reference to that node can be accessed in the ref’s current property.

const node = this.myRef.current;
Copy the code

The value of ref varies depending on the type of node:

  • whenrefAttribute is used in the constructor when used for HTML elementsReact.createRef()To create therefReceive the underlying DOM element as itcurrentProperties.
  • whenrefProperty for custom class components,refObject receives a mount instance of the component as itcurrentProperties.
  • You cannot use ref attributes on function components because they have no instances.

Add a ref to the DOM element

The following code uses ref to store a reference to a DOM node:

class CustomTextInput extends React.Component { constructor(props) { super(props); // create a ref to store the DOM element of textInput this.textinput = react.createref (); this.focusTextInput = this.focusTextInput.bind(this); } focusTextInput () {/ / text input box directly using the native API get focus / / note: we are through this. "current" to access the DOM node (. Current. The focus (); Return (<div> <input type="text") render() {// tell React we want <input> ref to be associated with 'textInput' created in // constructor ref={this.textInput} /> <input type="button" value="Focus the text input" onClick={this.focusTextInput} /> </div> ); }}Copy the code

React passes a DOM element to the current property when the component is mounted and a NULL value when the component is unmounted. The ref is updated before the componentDidMount or componentDidUpdate lifecycle hook is triggered.

We can make a rough guess by saying:

  • In the React source code, when the HTMl-parser is parsed here, there should be some logic to determine whether the current component is native to the DOM, and if so, assign the DOM tothis.textInput.current
  • In between the react component lifecycle execution sequence, there should be some logic to update the ref. So that we can be surerefWill be incomponentDidMountcomponentDidUpdateUpdates before lifecycle hooks trigger.

Add a Ref to the class component

If we wanted to wrap the CustomTextInput above to simulate it being clicked immediately after it is mounted, we could use ref to get the custom input component and manually call its focusTextInput method:

class AutoFocusTextInput extends React.Component { constructor(props) { super(props); this.textInput = React.createRef(); } componentDidMount() { this.textInput.current.focusTextInput(); } render() { return ( <CustomTextInput ref={this.textInput} /> ); }}Copy the code

Note that this only works if CustomTextInput is declared as class:

class CustomTextInput extends React.Component {  // ...
}
Copy the code

Using this example, we can go back to what we said earlier: when the REF attribute is used for a custom class component, the REF object receives the component’s mounted instance as its current attribute.

In this AutoFocusTextInput component, we create a new ref called textInput, but textInput. Current points to the instance that CustomTextInput actually mounted. New CustomTestInput() : textinput.current: textinput.current: textinput.current: textinput.current: textInput. Namely focusTextInput ()

Refs and function components

By default, you cannot use the ref attribute on function components because they have no instances:

function MyFunctionComponent() { return <input />; } class Parent extends React.Component { constructor(props) { super(props); this.textInput = React.createRef(); } render() { // This will *not* work! return ( <MyFunctionComponent ref={this.textInput} /> ); }}Copy the code

From this point of view, the above statement that the custom component actually calls new may be problematic.

If new is called directly, there are only two interpretations:

  • First, new Class is different from new Function. I will go back to the source code of Class for a while and fill in the holes.
  • Second, the React source code treats Class and Function differently, so Function declarations are not instantiated.

If you want to use refs in a function component, you can use forwardRef (which can be used in conjunction with useImperativeHandle), or you can convert the component to a class component.

However, you can use the ref attribute inside a function component as long as it points to a DOM element or class component:

Function CustomTextInput(props) {// textInput must be said so that ref can reference it const textInput = useRef(null); function handleClick() { textInput.current.focus(); } return ( <div> <input type="text" ref={textInput} /> <input type="button" value="Focus the text input" onClick={handleClick} /> </div> ); }Copy the code

The forwardRef will talk about Ref forwarding later.

Expose the DOM Refs to the parent component

In rare cases, you may want to reference the DOM node of the child node in the parent component. This is generally not recommended because it breaks the encapsulation of the component, but it can occasionally be used to trigger focus or measure the size or position of child DOM nodes.

Although you can add refs to child components, this is not an ideal solution because you can only get component instances, not DOM nodes. Also, it does not work on function components.

If you use React 16.3 or later, we recommend using ref forwarding in this case. Ref forwarding enables a component to expose its child’s Ref as well as its own. A detailed example of how to expose child component DOM nodes to the parent component is provided in the REF forwarding document.

If you use React version 16.2 or lower, or you need more flexibility than ref forwarding, you can use this alternative to pass ref directly as a prop with a special name.

Exposing DOM nodes is not recommended if possible, but sometimes it can be a lifesaver. Note that this scenario requires you to add some code to the child components. If you have no control over the implementation of child components, your only option is to use findDOMNode(), which is deprecated and not recommended in strict mode.

The callback Refs

React also supports another way to set refs, called “callback Refs.” It gives you more fine-grained control over when refs are set and released.

Instead of passing the ref attribute created by createRef(), you pass a function. This function takes React component instances or HTML DOM elements as arguments so that they can be stored and accessed elsewhere.

The following example describes a common example of using the ref callback function to store a reference to a DOM node in an instance property.

class CustomTextInput extends React.Component { constructor(props) { super(props); this.textInput = null; this.setTextInputRef = element => { this.textInput = element; }; This.focustextinput = () => {// Use the native DOM API to focus the text input box if (this.textinput) this.textinput.focus (); }; } componentDidMount() {// After the component is mounted, let the textbox automatically get focus this.focustextinPut (); } render() {// use the 'ref' callback to store a reference to the DOM node of the text input box on the React // instance (e.g. This.textinput) return (<div> <input type="text") ref={this.setTextInputRef} /> <input type="button" value="Focus the text input" onClick={this.focusTextInput} /> </div> ); }}Copy the code

React will call the ref callback and pass in the DOM element when the component is mounted and null when it is unmounted. React ensures that the refs are up to date before componentDidMount or componentDidUpdate is triggered.

There are some people here who might be trying to get a function called just because it’s passed in.

The React source code has special treatment for the ref attribute. If its value is a function, call it back.

If you do, then any function you pass in, it will execute, it doesn’t have to set ref in the function, it’s just a convention to set a ref in the function that the ref attribute passes in.

You can pass refs in the form of callbacks between components, just as you can pass refs for objects created through React. CreateRef ().

function CustomTextInput(props) { return ( <div> <input ref={props.inputRef} /> </div> ); } class Parent extends React.Component { render() { return ( <CustomTextInput inputRef={el => this.inputElement = el} /> ); }}Copy the code

In the example above, Parent passes its refs callback function to CustomTextInput as inputRef props, and CustomTextInput passes the same function to as a special ref attribute. As a result, this.inputElement in Parent is set to the DOM node corresponding to the input element in CustomTextInput.

Instructions on callbacks to refs

If the ref callback is defined as an inline function, it is executed twice during the update process, passing in the argument NULL the first time, and then the argument DOM element the second time. This is because a new function instance is created each time it renders, so React clears the old ref and sets the new one. This can be avoided by defining the ref callback function as a class binding function, but in most cases it is irrelevant.

Ref to forward

Ref forwarding is a technique for automatically passing a Ref through a component to one of its children. This is usually not required for components in most applications. But it can be useful for some components, especially reusable component libraries. The most common cases are described below.

Forward refs to the DOM component

Consider this FancyButton component that renders the native DOM element button:

function FancyButton(props) {
  return (
    <button className="FancyButton">
      {props.children}
    </button>
  );
}
Copy the code

The React component hides its implementation details, including its rendering results. Other components that use FancyButton usually do not need to get the ref of the internal DOM element button. This is good because it prevents the component from being overly dependent on the DOM structure of other components.

While this encapsulation is ideal for application-level components such as FeedStory or Comment, it can be inconvenient for highly reusable “leaf” components such as FancyButton or MyTextInput. These components tend to be used in a similar way to regular DOM buttons and inputs throughout the application, and access to their DOM nodes is unavoidable for managing focus, selection, or animation.

Ref forwarding is an optional feature that allows certain components to receiverefAnd passes it down (in other words, “forwards” it) to the child components.

In the following example, FancyButton uses the React. ForwardRef to get the ref passed to it and forward it to the DOM button it renders:

const FancyButton = React.forwardRef((props, ref) => ( <button ref={ref} className="FancyButton"> {props.children} </button> )); DOM button ref: const ref = react.createref (); <FancyButton ref={ref}>Click me! </FancyButton>;Copy the code

In this way, a component using FancyButton can get the ref of the underlying DOM node button and access it if necessary, just as if it were using DOM Button directly.

Here’s a step-by-step explanation of what happens in the example above:

  1. We do this by callingReact.createRefCreated aReact refAnd assign it torefThe variable.
  2. We do this by specifyingrefIs the JSX property, passed down to<FancyButton ref={ref}>.
  3. The React to passrefforwardRefWithin the function(props, ref) => ..., as its second parameter.
  4. We forward this downrefParameters to<button ref={ref}>, specifying it as a JSX property.
  5. When ref is mounted,ref.currentWill point to<button>The DOM node.

Pay attention to

The second argument, ref, only exists when the react. forwardRef component is defined. Regular functions and class components do not accept ref arguments, and ref does not exist in props.

Ref forwarding is not limited to DOM components; you can also forward refs to class component instances.