React Source Reading-2_031

Keywords: Javascript React Attribute

React Source Reading-2

In a typical React data stream, props is the only way for parent components to interact with their children. To modify a subitem, reproduce it using new props. However, in some cases, subitems need to be forced to be modified outside of the typical data stream. The child to be modified can be an instance of the react component or a DOM element. In both cases, React provides an api.

When to use refs

refs has some good use cases:

  • 1. Text selection or media playback.
  • 2. It is imperative to trigger animation.
  • 3. Integration with third-party DOM libraries.

Avoid using refs for any operations that can be done declaratively.

Don't overuse Refs

Old API: String Reference

If you have used React before, you may be familiar with an old API where the ref attribute is a string textInput and the DOM node is accessed as this.refs.textInput. It is not recommended to use it because string references have some problems that are considered legacy issues and are likely to be deleted in a future version.

Callback reference

When the component is installed, React calls the ref callback using DOM elements and null when uninstalled.
Refs is guaranteed to be up-to-date until componentDidMount or componentDidUpdate is triggered.

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);

    this.textInput = null;

    this.focusTextInput = () => {
      // Focus the text input using the raw DOM API
      if (this.textInput) this.textInput.focus();
    };
  }

  componentDidMount() {
    // autofocus the input on mount
    this.focusTextInput();
  }

  render() {
    // Use the `ref` callback to store a reference to the text input DOM
    // element in an instance field (for example, this.textInput).
    return (
      <div>
        <input
          type="text"
          ref={element => this.textInput = element}
        />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

Note on callback refs

If the ref callback function is defined as an inline function, it will be executed twice during the update process, first passing in the parameter null, and then the second passing in the parameter DOM element. This is because each rendering creates a new function instance, so React empties the old ref and sets the new one. This problem can be avoided by defining the callback function of ref as a class binding function, but in most cases it does not matter.

refs example - click to get input focus

class Example extends React.Component {
  handleClick() {
    // Use native DOM API to get focus
    this.refs.myInput.focus
    ();
  }
  render() {
    //  When a component is inserted into the DOM, the ref attribute adds a component reference to this.refs
    return (
      <div>
        <input type="text" ref="myInput" />
        <input
          type="button"
          value="Click my input box to get focus"
          onClick={this.handleClick.bind(this)}
        />
      </div>
    );
  }
}

Use React.createRef()

API introduced in React.createRef()React 16.3. If you are using an earlier version of React, we recommend that you use callback references.

Create React.createRef()

Refs are created using attributes, React.createRef() and attached to React elements through ref attributes. When building components, Refs are usually 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} />;
  }
}

Access reference

When ref is passed to the element, render makes the reference to the node accessible at the current tree's attribute


const node = this.myRef.current;

The value of ref varies depending on the type of node

  • When this attribute is used on refHTML elements, the attribute created by ref in the constructor receives the underlying DOM element as its current attribute.
  • When this property is used on a ref custom class component, the ref object receives the installed instance of the component as its current.

You may not be able to ref use this property on function components because they have no instances.

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    // create a ref to store the textInput DOM element
    this.textInput = React.createRef();
    this.focusTextInput = this.focusTextInput.bind(this);
  }

  focusTextInput() {
    // Explicitly focus the text input using the raw DOM API
    // Note: we're accessing "current" to get the DOM node
    this.textInput.current.focus();
  }

  render() {
    // tell React that we want to associate the <input> ref
    // with the `textInput` that we created in the constructor
    return (
      <div>
        <input
          type="text"
          ref={this.textInput} />

        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

When current is installed, React assigns DOM elements to this attribute and null assigns them back when uninstalled. CompoonentDidMount or CompoonentDidUpdate lifecycle methods before ref updates occur.

Source code

// an immutable object with a single mutable value
export function createRef(): RefObject {
  const refObject = {
    current: null,
  };
  if (__DEV__) {
    Object.seal(refObject);
  }
  return refObject;
}

Cannot ref use this property on function components

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} />
    );
  }
}

** If you need to refer to it, you should convert components into classes, just as you need lifecycle methods or states.
However, as long as DOM elements or class components are referenced, the ref attribute can be used in function components:

function CustomTextInput(props) {
  // textInput must be declared here so the ref can refer to it
  let textInput = React.createRef();

  function handleClick() {
    textInput.current.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={textInput} />

      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );
}

Exposing DOM references to parent components

In rare cases, you may want to access the DOM nodes of the child node from the parent component. This is not usually recommended because it destroys component encapsulation, but it can occasionally be used to trigger focus or measure the size or location of sub-DOM nodes.

Although references can be added to subcomponents, this is not an ideal solution because only component instances can be obtained, not DOM nodes. In addition, this does not apply to functional components.

If you use React 16.3 or later, we recommend ref forwarding in these cases. Reference forwarding allows components to choose to expose references to any subcomponent as their own components. Detailed examples of how to expose child DOM nodes to parent components can be found in ref forwarding documents.

If you use React 16.2 or lower, or if you need more flexibility than ref forwarding provides, you can use this alternative and explicitly pass ref as a prop with different names.

If possible, it is recommended not to expose DOM nodes, but it can be a useful escape capsule. Note that this method requires you to add some code to the subcomponent. If you have no control over the subcomponent implementation at all, the last option is to use find DOMNode (), but you are not encouraged to use StrictMode.

Posted by kutyadog on Thu, 10 Oct 2019 04:56:49 -0700