DEV Community

Cover image for ⚛️ Demystifying React's Types: Ref types
Will T.
Will T.

Posted on

⚛️ Demystifying React's Types: Ref types

When working with TypeScript in ReactJS, using refs in functional components requires a bit of extra care to ensure type safety. In this article, we'll explore the most popularly used and relevant types of refs available in TypeScript-based functional components.

1️⃣ React.RefObject

The React.RefObject type is the most common way to work with refs in functional components. It's used when you create a ref using the useRef hook and want to access the current value of the ref. A readonly ref container where the current property cannot be mutated.

Here's an example of using React.RefObject:

import { useRef } from 'react';

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

  const handleClick = () => {
    inputRef.current?.focus();
  };

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={handleClick}>Focus Input</button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

In this example, we create a ref called inputRef using the useRef hook and specify the type as HTMLInputElement.

Do not here that when using useRef, if the initially passed value is null, TypeScript will automatically understand it as a React.RefObject. If you want it to be React.MutableRefObject AND the initial value to be null, the simple trick is to simply define it as:

const inputRef = useRef<HTMLInputElement | null>(null);
Enter fullscreen mode Exit fullscreen mode

2️⃣ React.MutableRefObject

The React.MutableRefObject type is similar to React.RefObject, but it allows the current property of the ref to be mutable. This can be useful in scenarios where you need to update the ref's value directly, without triggering a re-render.

Here's an example of using React.MutableRefObject:

import { useRef } from 'react';

export const MyComponent = () => {
  const counterRef = useRef<number>(0);

  const handleClick = () => {
    counterRef.current++;
    console.log('Counter value:', counterRef.current++);
  };

  return (
    <div>
      <button onClick={handleClick}>Increment Counter</button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

3️⃣ React.RefCallback

The React.RefCallback type is used when you need to access the DOM element or React component at a specific point in time, such as when the component mounts or unmounts. This can be particularly useful when you're working with third-party libraries that require direct DOM access, or when you need to perform complex DOM manipulations, or when you need to manage a list of refs. Its type is defined as:

type RefCallback<T> = (instance:  T  |  null) =>  void;
Enter fullscreen mode Exit fullscreen mode

Below is an example of using React.RefCallback to measure the size of an element and update the component's state accordingly. This can be useful in scenarios where you need to implement responsive layouts or dynamic UI elements.

import { useState, useCallback } from 'react';

export const MyComponent = () => {
  const [elementSize, setElementSize] = useState<{ width: number; height: number }>({
    width: 0,
    height: 0,
  });

  const handleElementRef: React.RefCallback<HTMLDivElement> = useCallback((element) => {
    if (!element) return;

    const { offsetWidth, offsetHeight } = element;

    setElementSize({ width: offsetWidth, height: offsetHeight });
  }, []);

  return (
    <div ref={handleElementRef}>
      <p>Element size: {elementSize.width} x {elementSize.height}</p>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

4️⃣ React.Ref

React.Ref is basically a union type of all possible shapes for React refs. You can understand it as:

type  Ref<T> = RefCallBack<T> |  RefObject<T> |  null;
Enter fullscreen mode Exit fullscreen mode

5️⃣ React.ForwardedRef

The React.ForwardedRef type is used when you need to forward a ref from a parent component to a child component. This is particularly useful when you're working with reusable components and need to access the underlying DOM element or React component, or if there's a custom ref object created in the child component using useImperativeHandle.

Here's an example of using React.ForwardedRef:

import { forwardRef, Ref } from 'react';

interface InputProps {
  value: string;
  onChange: (value: string) => void;
}

const InputBase = ({ value, onChange }: InputProps, ref: React.ForwardedRef<HTMLInputElement>) => {
  return (
    <input
      type="text"
      value={value}
      onChange={(e) => onChange(e.target.value)}
      ref={ref}
    />
  );
});

export const Input = forwardRef(InputBase);
Enter fullscreen mode Exit fullscreen mode

In this example, we use the forwardRef function to create a ref-forwarding component, which allows the parent component to access the underlying input element.

6️⃣ React.LegacyRef

The React.LegacyRef type is used when you need to work with refs in a way that is compatible with older versions of React. This type is primarily used for class-based components, which are less common in modern React development.

It's important to note that the ref property of every primitive HTML element in React currently has the type React.LegacyRef<HTMLElementType>. This means that if you're working with a div element, the type of the ref property will most likely be React.LegacyRef<HTMLDivElement>.

You actually wouldn't have to care about this type at all in your React projects.

🏁 Conclusion

In this article, we've explored the most popular types of refs available in React, including React.RefObject, React.MutableRefObject, React.RefCallback, React.Ref, React.ForwardedRef, and React.LegacyRef. By understanding these types, you'll be more confident navigating a React project written in TypeScript. Stay tuned for more articles on React and TypeScript, where we'll dive into the latest features and best practices for building robust and scalable applications.

In case you think it was a good read, you'll probably find my previous post useful as well:


If you're interested in Frontend Development and Web Development with ReactJS in general, follow me and check out my articles in the profile below.

Top comments (5)

Collapse
 
seandinan profile image
Sean Dinan

Great write-up! As someone who's been migrating to Typescript, I've had quite a lot of trial & error with <type> vs <type | null> 😁

Collapse
 
itswillt profile image
Will T.

Thanks for reading the article! Years ago I also encountered the same issue. Hopefully this article will clear this up for a lot of people.

Collapse
 
friedrich482 profile image
Friedrich WT

Very interesting article ✨✨

Collapse
 
itswillt profile image
Will T.

Thanks! Hopefully it was helpful to you.

Collapse
 
v_h_e8a774ef1e1066a7e09 profile image
Vũ Hà

Very interesting