React refs with TypeScript


In this post, we cover how to use React refs with TypeScript in function and class components.

React Refs with TypeScript

Creating strongly-typed refs in function components

The useRef hook can be used to access all the properties and methods of an element.

const element = React.useRef(null);
// can access all the properties and methods of `element` via `element.current`
...
return (
<SomeElement ref={element} />
);

It is commonly used when we need to invoke methods on an element imperatively. Below is an example:

function Search() {
const input = React.useRef(null);
React.useEffect(() => {
if (input.current) {
input.current.focus();
}
}, []);
return (
<form>
<input ref={input} type="search" />
</form>
);
};

We are setting focus on an input when the component first renders.

The type of input.current is inferred as null if strict mode is on; otherwise, it is inferred as any. A type error also occurs when input.current is referenced if strict mode is on.

Not ideal. 😞

We can explicitly define the type of the element returned from useRef by passing a generic type parameter:

const element = React.useRef<ElementType>(null);

So, we can explicitly type the ref in the Search component as follows:

const input = React.useRef<HTMLInputElement>(null);

The type error now disappears. πŸ˜ƒ

πŸƒ Play with the code

Creating strongly-typed refs in class components

Let’s implement the Search component as a class component. Below is a first attempt:

class Search extends React.Component {
private input = React.useRef<HTMLInputElement>(null);
componentDidMount() {
if (this.input.current) {
this.input.current.focus();
}
}
render() {
return (
<form>
<input ref={this.input} type="type" />
</form>
);
}
}

The useRef hook can’t be used in class components though. 😞

In class components, we can get a reference to an element using createRef. We can use this to revise our implementation of the Search component:

class Search extends React.Component {
private input = React.createRef();
componentDidMount() {
if (this.input.current) {
this.input.current.focus();
}
}
render() {
return (
<form>
<input ref={this.input} type="type" />
</form>
);
}
}

The type of the input element is inferred to be unknown though. This means we get a type error when this.input.current is referenced. 😞

We can explicitly define the type of the element returned from createRef by passing a generic type parameter:

React.createRef<ElementType>();

A revised, more strongly-typed version of the Search component is below.

class Search extends React.Component {
private input = React.createRef<HTMLInputElement>();
componentDidMount() {
if (this.input.current) {
this.input.current.focus();
}
}
render() {
return (
<form>
<input ref={this.input} type="type" />
</form>
);
}
}

πŸƒ Play with the code

If we run the code, we will see that the focus is set on the input after it renders. πŸ˜ƒ

Learn React with TypeScript - 3rd Edition

New

A comprehensive guide to building modern React applications with TypeScript. Learn best practices, advanced patterns, and real-world development techniques.

View on Amazon
Learn React with TypeScript - Third Edition