A journey into the world of refs and ref forwarding in React
Tags: react, typescript
Published: 2022-05-30 17:09:43

In many React projects, we need to interact directly with the DOM.
For example, when we want to set the focus to an element. How to do that in React?
 
Let's start by creating a custom input component.
```javascript
function MyInput({ ...props }: React.InputHTMLAttributes<HTMLInputElement>) {
  return <input {...props} />;
}
```

Now, we want to get the focus after the component is mounted. To do that, we need to get access to the DOM element.
React refs are a way to get a reference to a DOM node. We use the useRef hook to create a ref object called _myInputRef_. Then, we add the ref to the input element.
_myInputRef_ enables us to call the focus method on the DOM element. As we want to get the focus after it's mounted, we need to use the useEffect hook. We end up with the following code:
```javascript
function MyInput({ ...props }: React.InputHTMLAttributes<HTMLInputElement>) {
  const myInputRef = React.useRef<HTMLInputElement>(null);
  React.useEffect(() => {
    if (myInputRef.current) {
      myInputRef.current.focus();
    }
  }, []);
  return <input ref={myInputRef} {...props} />;
}
```

Imagine that our goal is to create a generic custom component. We do not want to get the focus each time we use our React component. The idea is to be more flexible. We would like to expose a ref and let the parent component decide what to do (set the focus or not).
The naive approach is to add a ref property to _MyInput_.
```javascript
function MyInput({
  ref,
  ...props
}: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> ) {
  return <input ref={ref} {...props} />;
}
```
Here's the App component that uses _MyInput_:
```javascript
function App() {
  const inputRef = useRef<HTMLInputElement>(null);

  React.useEffect(() => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []);

  return (
    <div>
      <MyInput ref={inputRef} />
    </div>
  );
}
```
It does not work, and we get the following warning:
> Warning: MyInput: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop.

We cannot use a prop called _ref_, so let's rename it. Here's what the code would be like:
```javascript
function MyInput({
  myRef,
  ...props
}: React.InputHTMLAttributes<HTMLInputElement> & {
  myRef: React.Ref<HTMLInputElement>;
}) {
  return <input ref={myRef} {...props} />;
}

function App() {
  const inputRef = useRef<HTMLInputElement>(null);

  React.useEffect(() => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []);

  return (
    <div>
      <MyInput myRef={inputRef} />
    </div>
  );
}
```

It works. But according to the React documentation, it's not the recommended solution. A component should use the _ref_ property to be compatible with other components and libraries.
_Ref_ is not available in props, so we must wrap the component in forwardRef. Now, our function component accepts _ref_ as the second argument.

Let's refactor our code to use forwardRef instead. Here's the final version:

```javascript
type MyInputProps = React.DetailedHTMLProps<
  React.InputHTMLAttributes<HTMLInputElement>,
  HTMLInputElement
>;
const MyInput = React.forwardRef<HTMLInputElement, MyInputProps>(function (
  { ...props },
  ref
) {
  return <input ref={ref} {...props} />;
});

function App() {
  const inputRef = useRef<HTMLInputElement>(null);

  React.useEffect(() => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []);

  return (
    <div>
      <MyInput ref={inputRef} />
    </div>
  );
}
```
This technique for automatically passing a ref through a component to one of its children is called ref forwarding.

I hope that gives you an idea of a good way to use React forwardRef in Typescript.