sajad torkamani

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

Tagged: React