Refs provide a way to access DOM nodes (or React elements) created in the render method, but why would you forward a reference?
Forwarding refs
Ref forwarding in React means, that a reference through a component is automatically passed to one of its children. Typically, this is not necessary for most components, but sometimes, it is very useful. Let's find out why.
Forwarding refs to DOM components
Forwarding refs can be useful in reusable component libraries. Let's consider an AwesomeButton
component, that renders the native button DOM element.
functon AwesomeButton(props) {
return(
<button>{props.children}</button>
)
}
React components hide their implementation details, including their rendered output. This means that other components using the AwesomeButton
, will usually not obtain a ref to the inner DOM element. This encapsulation is good, because it prevents components from relying heavily on each other's DOM structure. A high level of encapsulation is desirable on application-level, it can be unpractical for highly reusable leaf components (think of the React tree). These leaf components like AwesomeButton
are used like native DOM elements, like a button, and managing focus, selection or animations require access to their DOM nodes.
Ref-forwarding lets some components take a ref they receive, and pass it further down (forward it) to a child. In the example below the ref passed to AwesomeButton
is forwarded down to the DOM button, which gives components using the AwesomeButton
component access to the button DOM node, just like they would use a DOM button directly.
const AwesomeButton = React.forwardRef((props, ref) => (
<button ref={ref}>{props.children}</button>
));
const ref = React.useRef();
<AwesomeButton ref={ref}>Click it</AwesomeButton>;
After attaching the ref
, ref.current
will point to the <button>
DOM element.
Forwarding refs in higher-order components
Higher-Order Components or HOCs can benefit from this ref forwarding technique. Let's have a look at a HOC example, which logs component props to the console.
function logProps(WrappedComponent) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
return <WrappedComponent {...this.props} />;
}
}
return LogProps;
}
The logProps
HOC passes all props through the wrapped components and, it doesn't affect the rendered output. Let's apply this on the AwesomeButton
.
class AwesomeButton extends React.Component {
//...
}
export default logProps(AwesomeButton);
Then we import the AwesomeButton and apply a reference.
import AwesomeButton from './AwesomeButton';
//...
const ref = createRef();
//...
<AwesomeButton
label="click it"
handleClick={handleClick}
ref={ref}
/>;
There is one thing to consider, ref
is not a prop. The reference passed to AwesomeButton
, which is now a Higher-Order-Component, will not be passed down, because ref is not a prop. Instead, the reference will be attached to the HOC logProps
.
To avoid this we can explicitly forward refs to the inner AwesomeButton
component using forwardRef
. The React.forwardRef
API receives props
and ref
parameters and returns a React.node
.
function logProps(Component) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
const { forwardedRef, ...rest } = this.props;
// Assign the custom prop "forwardedRef" as a ref
return <Component ref={forwardedRef} {...rest} />;
}
}
// The second parameter in forwardRef can be used as a normal prop.
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
}
TL;DR
- In React
ref
andkey
are handled differently. - The
React.forwardRef
API receivesprops
andref
parameters and returns aReact.node
. - In HOC, it's important to know that
ref
is not a prop and will not be automatically forwarded with other props.React.forwardRef
has to be used.
Thanks for reading and if you have any questions , use the comment function or send me a message @mariokandut. If you want to know more about React, have a look at these React Tutorials.
References (and Big thanks):
React Docs - Forwarding Refs,React Docs - Refs,Bits and Pieces,LogRocket
Top comments (1)
@mario 's content is always 100+ karma points !