ReactHustle

How to use React useRef in Typescript

Jasser Mark Arioste

Jasser Mark Arioste

How to use React useRef in Typescript

In my previous tutorial, I showed you everything you need to know about how to use useState in typescript.

In this beginner guide, I'm going to show you all you need to know about using React's useRef hook in typescript. When I was new to typescript, I didn't know how to use useRef. I was encountering errors on how to initialize it when referring to an HTML element. 

We're going start from the very beginning up to the more advanced usage of useRef. At the end of this tutorial, we'll be able to master how to useRef hook in typescript.

What is the useRef hook? #

The useRef hook allows you to persist values between re-renders. When changing its value, it does not trigger a re-render of a component. The most common use case is to store a reference to an HTML element to be able to access its dom API.

How to use the useRef for Basic HTML Elements #

Let's take a look at a basic example. Below, we are setting the focus to the input element when a component renders for the first time. 

import React, { useRef, useEffect } from "react";

const Component = () => {
  const inputRef = useRef(null);
    useEffect(() => {
    if (inputRef.current) {
      inputRef.current.focus(); 
    }
  }, []);

  return (
    <div>
      <input type="text" ref={inputRef} />
    </div>
  );
};

export default Component;
123456789101112131415161718

The code above will be fine for Javascript. But, what are the issues of the above code in typescript?

The result is a compile-time error. The typescript compiler does not know that inputRef.current is an HTML Element.

...
inputRef.current.focus(); //Error: Property 'focus' does not exist on type 'never'.
...
123

To fix this, we have to declare the type of value we want to store inside useRef by using a generic type parameter. Let's change our useRef initialization and you'll see that the error is gone.

...
//const inputRef = useRef(null);
const inputRef = useRef<HTMLInputElement>(null);
...
inputRef.current.focus(); //no error
12345

How to determine the generic type parameter for useRef? #

In VSCode Editor, you can see the types of HTML elements if you hover over the ref attribute. Use this, as the type parameter for the useRef hook.

Hovering over ref shows the generic type parameter for the element

Some examples:

const inputRef = useRef<HTMLInputElement>(null);
const divRef = useRef<HTMLDivElement>(null);
const formRef = useRef<HTMLFormElement>(null);
123

Type 'MutableRefObject<HTMLInputElement | undefined>' is not assignable to type 'LegacyRef<HTMLInputElement> | undefined'.  Error #

Sometimes we fail to initialize useRef properly and the error above shows up. Most likely, you did not initialize useRef to null. To fix this, change the following:


//const inputRef = useRef<HTMLInputElement>(); 
const inputRef = useRef<HTMLInputElement>(null); 
123

TIP: As a best practice, always initialize useRef to null for HTML elements.

Object is possibly 'null'. Error #

React manages the reference for HTML elements and will populate the .current property. We are using inputRef in the input element. React populates this and it should not be null as typescript suggests . 


import React, { useEffect, useRef } from "react";

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

  useEffect(() => {
      inputRef.current.focus(); //Object is possibly 'null'
  }, []);

  return (
    <div>
      <input type="text" ref={inputRef} />
    </div>
  );
};

export default Component;
123456789101112131415161718

There are three possible solutions to this error.

If we are sure that the reference will not be null as in the example above, we can simply use the non-null assertion operator ! .


...
//const inputRef = useRef<HTMLInputElement>(null);
const inputRef = useRef<HTMLInputElement>(null!);
...
useEffect(() => {
      inputRef.current.focus(); //no error
  }, []);
12345678

Otherwise, the solution is to add a guard if-statement or use the optional chaining operator when accessing the reference.


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

//or 
inputRef.current?.focus();  //no error
1234567891011

How to use useRef for third-party UI libraries like MUI #

Oftentimes, we use third-party libraries like MUI to speed up development. Sometimes, we get stuck when using useRef too due to long typescript errors.

Let's take a look at a simple example where we want to focus on a Button component after rendering.

import { Button } from "@mui/material";
import React, { useEffect, useRef } from "react";

const Component = () => {
  const buttonRef = useRef(null); //no generic type here
  useEffect(() => {
    if (buttonRef.current) {
      buttonRef.current.focus(); //Error: //Property 'focus' does not exist on type 'never'.
    }
  }, []);

  return (
    <div>
      <Button ref={buttonRef} />
    </div>
  );
};

export default Component;
12345678910111213141516171819

To fix the error above, we learned previously that we need to use a generic type parameter. If we try to use a broard type like <HTMLElement> , it will fix the error above but also produces another error.

...
const buttonRef = useRef<HTMLElement>(null); 
...

  return (
    <div>
      <Button ref={buttonRef} /> //Error: Type 'RefObject<HTMLElement>' is not assignable to type 'RefObject<HTMLButtonElement>'.
    </div>
  );
..
12345678910

Type 'RefObject<HTMLElement>' is not assignable to type 'RefObject<HTMLButtonElement>'. #

This error occurs when we are not using a more specific HTML element or simply the wrong element type.

The solution is already stated in the error itself. We have to change useRef to use <HTMLButtonElement> as type or whatever is stated in the error.

...
const buttonRef = useRef<HTMLButtonElement>(null); 
...

  return (
    <div>
      <Button ref={buttonRef} /> //no error
  );
...
123456789

To avoid this error, you can hover over the ref property of the component and initialize useRef with that specific type.

Hovering over the Button component ref tells us the RefObject type.
//we know that the Button ref prop expects a React.RefObject<HTMLButtonElement>
//lets use <HTMLButtonElement> as the type for our useRef hook.
const buttonRef = useRef<HTMLButtonElement>(null!);
123

Conclusion #

We learned the best practices on how to use the useRef hook with typescript. We also learned how to deal with possible errors when using useRef.

If you like this tutorial, please leave a like or share! If you want to get updated on our latest react or javascript tutorials. Subscribe to our newsletter!

Resources #


Credits - Image by David Mark from Pixabay

Share this post!

Related Posts