Forwarding Refs
Ref forwarding is a technique for automatically passing a ref through a component to one of its children. This is typically not necessary for most components in the application. However, it can be useful for some kinds of components, especially in reusable component libraries. The most common scenarios are described below.
Forwarding refs to DOM components
Consider a FancyButton component that renders the native button DOM element:
function FancyButton(props) {
return (
<button className="FancyButton">
{props.children}
</button>
);
}
React components hide their implementation details, including their rendered output. Other components using FancyButton usually will not need to obtain a ref to the inner button DOM element. This is good because it prevents components from relying on each other’s DOM structure too much.
Although such encapsulation is desirable for application-level components like FeedStory or Comment, it can be inconvenient for highly reusable “leaf” components like FancyButton or MyTextInput. These components tend to be used throughout the application in a similar manner as a regular DOM button and input, and accessing their DOM nodes may be unavoidable for managing focus, selection, or animations.
Ref forwarding is an opt-in feature that lets some components take a ref they receive, and pass it further down (in other words, “forward” it) to a child.
In the example below, FancyButton uses React.forwardRef to obtain the ref passed to it, and then forward it to the DOM button that it renders:
const FancyButton = React.forwardRef((props, ref) => ( <button ref={ref} className="FancyButton"> {props.children}
</button>
));
// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
This way, components using FancyButton can get a ref to the underlying button DOM node and access it if necessary—just like if they used a DOM button directly.
Here is a step-by-step explanation of what happens in the above example:
- We create a React ref by calling
React.createRefand assign it to arefvariable. - We pass our
refdown to<FancyButton ref={ref}>by specifying it as a JSX attribute. - React passes the
refto the(props, ref) => ...function insideforwardRefas a second argument. - We forward this
refargument down to<button ref={ref}>by specifying it as a JSX attribute. - When the ref is attached,
ref.currentwill point to the<button>DOM node.
Note
The second
refargument only exists when you define a component withReact.forwardRefcall. Regular components don’t receive therefargument, and ref is not available in props either.Ref forwarding is not limited to DOM components. You can forward refs to components you have created, too.
Note for component library maintainers
When you start using forwardRef in a component library, you should treat it as a breaking change and release a new major version of your library. This is because your library likely has an observably different behavior (such as what refs get assigned to, and what types are exported), and this can break apps and other libraries that depend on the old behavior.
Conditionally applying React.forwardRef when it exists is also not recommended for the same reasons: it changes how your library behaves and can break your users’ apps when they upgrade React itself.
Displaying a custom name in DevTools
React.forwardRef accepts a render function. React DevTools uses this function to determine what to display for the ref forwarding component.
For example, the following component will appear as ”ForwardRef” in the DevTools:
const WrappedComponent = React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
If you name the render function, DevTools will also include its name (e.g. ”ForwardRef(myFunction)”):
const WrappedComponent = React.forwardRef(
function myFunction(props, ref) {
return <LogProps {...props} forwardedRef={ref} />;
}
);
You can even set the function’s displayName property to include the component you’re wrapping:
function logProps(Component) {
function LogProps(props) {
// ...
}
function forwardRef(props, ref) {
return <LogProps {...props} forwardedRef={ref} />;
}
// Give this component a more helpful display name in DevTools.
// e.g. "ForwardRef(logProps(MyComponent))"
const name = Component.displayName || Component.name; forwardRef.displayName = `logProps(${name})`;
return React.forwardRef(forwardRef);
}