Instantly share code, notes, and snippets.

Embed
What would you like to do?

React 16.2 and earlier doesn't yet support ref forwarding. If you need to expose a DOM ref to components above and can't migrate to React 16.3+ yet, you can use the following approach instead.

Expose a special prop on the child. This prop can be named anything other than ref (e.g. inputRef). The child component can then forward the prop to the DOM node as a ref attribute. This lets the parent pass its ref to the child's DOM node through the component in the middle.

This works both for classes and for functional components.

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.inputElement = React.createRef();
  }
  render() {
    return (
      <CustomTextInput inputRef={this.inputElement} />
    );
  }
}

In the example above, Parent passes its class property this.inputElement as an inputRef prop to the CustomTextInput, and the CustomTextInput passes the same ref as a special ref attribute to the <input>. As a result, this.inputElement.current in Parent will be set to the DOM node corresponding to the <input> element in the CustomTextInput.

Note that the name of the inputRef prop in the above example has no special meaning, as it is a regular component prop. However, using the ref attribute on the <input> itself is important, as it tells React to attach a ref to its DOM node.

This works even though CustomTextInput is a functional component. Unlike the special ref attribute which can only be specified for DOM elements and for class components, there are no restrictions on regular component props like inputRef.

Another benefit of this pattern is that it works several components deep. For example, imagine Parent didn't need that DOM node, but a component that rendered Parent (let's call it Grandparent) needed access to it. Then we could let the Grandparent specify the inputRef prop to the Parent, and let Parent "forward" it to the CustomTextInput:

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

function Parent(props) {
  return (
    <div>
      My input: <CustomTextInput inputRef={props.inputRef} />
    </div>
  );
}

class Grandparent extends React.Component {
  constructor(props) {
    super(props);
    this.inputElement = React.createRef();
  }
  render() {
    return (
      <Parent inputRef={this.inputElement} />
    );
  }
}

Here, the ref this.inputElement is first specified by Grandparent. It is passed to the Parent as a regular prop called inputRef, and the Parent passes it to the CustomTextInput as a prop too. Finally, the CustomTextInput reads the inputRef prop and attaches the passed ref as a ref attribute to the <input>. As a result, this.inputElement.current in Grandparent will be set to the DOM node corresponding to the <input> element in the CustomTextInput.

@ackvf

This comment has been minimized.

ackvf commented Apr 16, 2018

These examples use React.createRef(), but it's not available pre 16.3...?

@bpartridge

This comment has been minimized.

bpartridge commented Apr 17, 2018

@ackvf Just have Grandparent pass inputRef={(el) => this.inputElement = el}. It's just another layer of indirection on top of the second example at https://reactjs.org/docs/refs-and-the-dom.html#callback-refs

@knee-cola

This comment has been minimized.

knee-cola commented Oct 3, 2018

What are benefits of using React.forwardRef instead of approach presented here?

I mean, React.forwardRef can be used directly only on function components. To be used with class components an additional shim needs to be added.

@mherodev

This comment has been minimized.

mherodev commented Oct 17, 2018

I'd also love to understand why I'd want to use forwardRef over this approach. I've done it by recommendation, but it's awkward the second you want to pass multiple refs down... then this pattern (or the callback pattern, historically) seem better because they scale.

The best argument I have is that multiple refs for a single component are an anti-pattern, perhaps?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment