TypeScript: React useRef Hook

 by Robin Wieruch
 - Edit this Post

A brief summary of how to use React's useRef Hook for using a ref with TypeScript. First, a ref in React is mainly used to assign a HTML element to it. The assigned HTML element gives us imperative read and write operations on it which allows us to programmatically call functions. Take the following example for focusing an input element:

import * as React from 'react';
const App = () => {
const ref = React.useRef();
React.useEffect(() => {
if (ref.current) {
ref.current.focus();
}
}, []);
return <input ref={ref} />;
};
export default App;

When using this with its useRef hook in TypeScript, you will most likely encounter an error. The best practice would be initializing the ref with null. In addition, you have to use a type argument to cast the ref to the type of HTMLInputElement respectively to the element where it is used as ref attribute:

import * as React from 'react';
const App = () => {
const ref = React.useRef<HTMLInputElement>(null);
React.useEffect(() => {
if (ref.current) {
ref.current.focus();
}
}, []);
return <input ref={ref} />;
};
export default App;

Essentially that's already it if you want to use an immutable ref for the HTML element. However, sometimes you want to use a to capture a value. For example, a ref could keep track of all click interactions:

import * as React from 'react';
const App = () => {
const [count, setCount] = React.useState<number>(0);
const ref = React.useRef<number>(0);
const handleIncrement = () => {
ref.current++;
setCount(count + 1);
};
const handleDecrement = () => {
ref.current++;
setCount(count - 1);
};
return (
<>
<button onClick={handleIncrement}>+</button>
<button onClick={handleDecrement}>-</button>
<div>Count: {count}</div>
<div>Buttons {ref.current} times clicked</div>
</>
);
};
export default App;
Count: 0
Buttons 0 times clicked

Similar example but with a complex object where we extracted the type argument as interface:

import * as React from 'react';
interface CounterTracker {
increment: number;
decrement: number;
}
const App = () => {
const [count, setCount] = React.useState<number>(0);
const ref = React.useRef<CounterTracker>({
increment: 0,
decrement: 0,
});
const handleIncrement = () => {
ref.current.increment++;
setCount(count + 1);
};
const handleDecrement = () => {
ref.current.decrement++;
setCount(count - 1);
};
return (
<>
<button onClick={handleIncrement}>+</button>
<button onClick={handleDecrement}>-</button>
<div>Count: {count}</div>
<div>
Buttons {ref.current.increment + ref.current.decrement}{' '}
times clicked
</div>
<div>Increment clicked: {ref.current.increment}</div>
<div>Decrement clicked: {ref.current.decrement}</div>
</>
);
};
export default App;
Count: 0
Buttons 0 times clicked
Increment clicked: 0
Decrement clicked: 0

If you happen to start with an instance variable that's not initialized in React's useRef Hook, but later in the code, then you would have to initialize React's useRef Hook with null and use a union type based on the actual type and null for the type argument:

import * as React from 'react';
const App = () => {
const [seconds, setSeconds] = React.useState<number>(0);
const [toggle, setToggle] = React.useState<boolean>(false);
const ref = React.useRef<NodeJS.Timeout | null>(null);
const toggleStopwatch = () => {
setToggle(!toggle);
};
const resetStopwatch = () => {
setToggle(false);
setSeconds(0);
};
React.useEffect(() => {
ref.current = setInterval(() => {
if (toggle) setSeconds((state) => state + 1);
}, 1000);
return () => {
if (ref.current) clearInterval(ref.current);
};
}, [toggle]);
return (
<>
<div>{seconds}</div>
<button onClick={toggleStopwatch}>
{toggle ? 'Stop' : 'Start'}
</button>
<button onClick={resetStopwatch}>Reset</button>
</>
);
};
export default App;
0

Essentially that's everything you need to know about using TypeScript, React's useRef Hook, and React's ref. After all, either the ref is used as HTML element by leveraging the ref attribute on an element or as instance variable to keep track of a state which does not cause React to re-render. If you happen to find other variations of using React's ref with TypeScript, let me know and I will add them to this guide.

Keep reading about 

A brief walkthrough on how to upgrade Vite from JavaScript to TypeScript. The tutorial assumes that you have already created a React project with Vite in JavaScript. To use TypeScript in React (with…

TypeScript is getting more popular these days for frontend and backend applications. Here you will learn how to set up TypeScript in Node.js for a backend project. The previous tutorial already…

The Road to React

Learn React by building real world applications. No setup configuration. No tooling. Plain React in 200+ pages of learning material. Learn React like 50.000+ readers.

Get it on Amazon.