React - Forwarding multiple refs

I actually just picked this up from react-hook-form, but they presented a nice way to share ref and assign multiple refs at once:

<input name="firstName" ref={(e) => {
  register(e) // use ref for register function
  firstNameRef.current = e // and you can still assign to ref
}} />

I know there is an already accepted answer, and while I find @nicholas-haley's solution acceptable. I think a better way to go about it would be to use the built-in useImperativeHandle hook.

IMPORTANT: The React Hooks Api is available as of

The React hooks API Docs state:

useImperativeHandle customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeHandle should be used with `forwardRef

This note is followed by the following example:

function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);

Thus, in my opinion, a much cleaner solution would be to delegate the needed refs through the useImperativeHandle hook.

This way there is no need for a special ref syntax, and the component can simply return a specific type of a FatherRef; Example:

// LabelInput.js
function LabelInput(props, ref) {
    const labelRef = useRef();
    const inputRef = useRef();

    useImperativeHandle(ref, () => ({
      focus: () => {
       inputRef.current.focus();
      },
      get input() {
          return inputRef.current;
      },
      get label() {
          return labelRef.current;
      },
      // ... whatever else one may need
    }));
    return (
      <div>
        <label ref={labelRef} ... />
        <input ref={inputRef} ... />;
      </div>
    )
}
LabelInput = forwardRef(LabelInput);

function MyScreen() {
   const labelInputRef = useRef();

   const onClick = useCallback(
    () => {
//       labelInputRef.current.focus(); // works
//       labelInputRef.current.input.focus(); // works
// ... etc
    },
    []
   );

   return (
     ...
      <LabelInput ref={labelInputRef} ... />
     ....
   )
}

I had a similar situation where I needed multiple refs to be forwarded to the child of my Parent component.

I still have not found an elegant solution, however you might try passing your refs as an object, and destructuring within the forwardRef callback:

// Parent
ref={{
    ref1: this.ref1,
    ref2: this.ref2
}}

// Child
export default React.forwardRef((props, ref) => {
  const { ref1, ref2 } = ref;

  return (
    <Child1
      {...props}
      ref={ref1}
    />
    <Child2
      {...props}
      ref={ref2}
    />
  );
});

I'm not a big fan of the naming here (I'd prefer ref to be called refs), but this works in case you're in a pinch.

EDIT:

In 2020 I believe @samer-murad's answer is the best solution to this problem.

Tags:

Reactjs