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>
);
};
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);
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>
);
};
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;
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>
);
};
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;
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);
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)
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>
😁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.
Very interesting article ✨✨
Thanks! Hopefully it was helpful to you.
Very interesting