How to use the useState React hook with Typescript

Felix TellmannFT

Felix Tellmann / 2020-01-09

3 min read

React Hooks have been around for a while now, yet they are still as awesome as on day one. With the number of custom hooks developed by the community, they are like a superpower for any developer. Pretty awesome, right?

The change to React Hooks, which came out in version 16.8, has had some massive impact on choosing a programming paradigm, giving Functional Programming a new edge.

Let's find out how we can use the useState hook and how it works with Typescript.

If you are new to React Hooks, check out the Official Docs.

The useState hook is by far the react hook I use the most often. Using the hook allows us to build entire apps only using Functional Components and without any supersized class based components that hold the state of the entire application.

Here's how the useState() API works: we load the useState function from react and initiate it within a Functional Component const [number, setNumber] = useState(0) the function takes in one argument as the intial state and returns an array with two items: the variable containing the state and a function to set the state.

Here is a plain javascript example:

example.jsx
1import React, { useState } from 'react';
2
3const Counter = () => {
4 const [number, setNumber] = useState(0);
5
6 return (
7 <div>
8 <p>Your number is {number}</p>
9 <button onClick={() => setNumber(number + 1)}>Increase</button>
10 <button onClick={() => setNumber(number - 1)}>Decrease</button>
11 </div>
12 );
13};

Try it out:

You can add as many useState() calls as you want. The main limitation is that the calls have to be made within the top level of the Functional Component. It is not possible to call react hooks conditionally or in a nested block.

Adding types to components is a great way to handle increased complexity as a developer. It's like telling your future self what needs to be done where and when. In the above example, we wouldn't need to add a single type annotation as all the necessary types are inferred.

Type inference is the ability to automatically deduce, either partially or fully, the type of an expression at compile time. The compiler is often able to infer the type of a variable or the type signature of a function, without explicit type annotations having been given.

The most practical type we could add is the initial state. This is especially useful if there isn't any initial value. Here is a quick example:

example.tsx
1import { ChangeEvent, FC, useState } from "react";
2
3const Welcome: FC = () => {
4 const [name, setName] = useState<string>()
5
6 return (
7 <div>
8 <p>Please type in your name:</p>
9 <input type="text" onChange={(e: ChangeEvent<HTMLInputElement>) => setName(e.currentTarget.value)}/>
10 <h2>Welcome {name}</h2>
11 </div>
12 )
13}