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
.
With respect to the comment made by @gaearon - "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."
This seems little misleading. Even in the case of React.forwardRef, we can still use 'ref' attribute on parent functional component but the child should finally have a 'ref' attribute equated to forwaredRef value and this child should either be a html DOM element or React class element. Hint: See the example below.
Also this alternative prop based ref forwarding discussed above has the same fundamental. The final child , whatever be the depth, that gets the 'ref' attribute ( In this pattern 'ref' is used only once and that too at the final child level), has to be a html DOM element or React class element.
Therefore I dont see any advantage of this alternative approach over Ref forwarding wrt the statement of the author.
However, I completely agree with the benefit of multilevel ref forwarding available with this approach.
Example-