Using React.forwardRef() and an HOC on the same component
Tags: react, typescript
Published: 2020-02-13 19:07:20

Focus management in React currently has one solution: [refs](https://reactjs.org/docs/refs-and-the-dom.html). If you want a function component to accept a ref, you should use [React.forwardRef()](https://reactjs.org/docs/forwarding-refs.html). For a basic Input component, it would look like this:

```jsx
import React from "react";

const Input = React.forwardRef(function Input({name, type, id, disabled, ...props}, ref) {
  return (
    <input
      {...props}
      name={name}
      id={id}
      disabled={disabled}
      type={type}
      ref={ref} />
  );
});

export default Input;
```

That's great. But what if we wanted to wrap it in an HOC? Maybe we have an HOC, shared between different form controls, for handling status messages. Let's call is `withStatusMessages()`. Normally, we would do something like this:

```jsx
export default withStatusMessages(Input);
```

Everything will compile, but our `ref` stops working and we'll see an error in the console about function components not accepting refs.

What happened?

Remember, the component passed to `React.forwardRef()` needs to accept two parameters, with the second one being the `ref`. But our HOC doesn't know that, it just accepts and passes on `props`. We could update our HOC to pass on refs, but we might want it to be used with components that don't accept refs. So what can we do?

We've already decided that we can't apply the HOC after `React.forwardRef()` which means we have to apply the HOC before `React.forwardRef()`. We can't just have `withStatusMessages(React.forwardRef())` because then our HOC will still drop the `ref` and not pass it on. What we need is a way to have the `ref` from `React.forwardRef()` passed on to the component via props (instead of as a second argument). Here's what I've come up with: 

```jsx
const Input = withStatusMessages(function Input({
  name,
  type,
  id,
  disabled,
  inputRef,
  ...props
}) {
  return (
    <input
      {...props}
      name={name}
      id={id}
      disabled={disabled}
      type={type}
      ref={inputRef}
    />
  );
});

export default React.forwardRef((props, ref) => {
  return <Input {...props} inputRef={ref} />;
});
```

We pass in the `ref` as the `inputRef` prop and then attach it to the `input` as normal. Now we get to use both `React.forwardRef()` and an HOC on the same component.

Note that we have to _rename_ the prop, we can't just keep `ref`. In other words, we can't do this:

```jsx
export default React.forwardRef((props, ref) => {
  return <Input {...props} ref={ref} />;
});
```

If you do, you'll get the error about function components not accepting refs because `ref` is handled specially by React (I don't know why, it sure would be nice if it just worked).

I created [a working example](https://codesandbox.io/s/charming-lamarr-zpzxm) of using this technique. And if you use TypeScript, the types are not straightforward so I've got you covered with [the same example in TypeScript](https://codesandbox.io/s/sleepy-kilby-1zyof).

Do you know of a better way to handle this situation? I'd love to hear it.