What is ref forwarding in React?
In a nutshell
Ref forwarding is the technique of creating a component that receives a ref, and passing (“fowarding”) that ref down to a child (usually DOM node).
Here’s an example of ref forwarding:
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>;
FancyButton
uses React.forwardRef
to obtain a ref passed to it, and passes down that ref to a child button
DOM element.
Now, components that use FancyButton
can use a ref to access the underlying button
element. The ref is said to be “forwarded” from the component that uses FancyButton
down to the button
DOM element.
When to use ref forwarding?
Use ref forwarding when you need to access a native DOM element of a component from a parent component. For example, you might have a <TextField />
component that renders an <input type="text" />
element. If you want to access that <input />
element from a component higher in the hierarchy than <TextField />
, you can use ref forwarding.
In most cases, React components should hide their implementation details so that other components that use them don’t rely heavily on their DOM structure.
But refer forwarding provides an escape hatch for when you need to do things like access an <input />
element to manage its focus, selection, animation, etc.
Example of ref forwarding in TypeScript
Suppose you want to create a <TextInput>
component that behaves exactly like an <input>
element, but has application-specific styling:
// TextInput.tsx
import React from 'react'
import classNames from 'classnames'
type Props = React.ComponentPropsWithoutRef<'input'>
const TextInput = React.forwardRef<HTMLInputElement, Props>(
({ className, ...props }, ref) => (
<input
ref={ref}
{...props}
className={classNames('p-2 border block mb-3', className)}
/>
)
)
export default TextInput
You can then use it like so:
<form>
<TextInput placeholder="Name" className="foo" required />
<TextInput placeholder="Email" />
// etc..
</form>
Your <TextInput>
component will take all the usual props that an <input>
element does.
Sources
Thanks for your comment 🙏. Once it's approved, it will appear here.
Leave a comment