Skip to main content

TypeScript: React

Resources​

A great resource on TypeScript in React is available here:

πŸ“˜ react.dev > typescript

a useful cheatsheet is available here:

πŸ“˜ React TypeScript Cheatsheet

a very useful sandbox is available here:

πŸ”— TypeScript Playground

props​

Let's start with a simple example of a functional component that takes a prop. First we will look at the standard JavaScript version, and then we will modify it to use TypeScript.

MyButton.tsx
function MyButton({ title }) {
return <button>{title}</button>;
}

In a .tsx component we must define the type of the prop.

MyButton.tsx
function MyButton({ title }: { title: string }) {
return <button>{title}</button>;
}

or we could use an interface.

MyButton.tsx
interface MyButtonProps {
title: string;
}

function MyButton({ title }: MyButtonProps) {
return <button>{title}</button>;
}

Hooks​

React includes a set of built in type definitions for your hooks. However there are times where we will want to explicitly define the type of a hook, or we will need to import the type from a library that we are using.

useState​

Basic Usage​

The useState Hook will re-use the value passed in as the initial state to determine what the type of the value should be. For example:

// Infer the type as "boolean"
const [enabled, setEnabled] = useState(false);

we can also explicitly define the type.

// Explicitly set the type to "boolean"
const [enabled, setEnabled] = useState<boolean>(false);

Union Types​

or a more useful instance of this would be defining a union type for the state to set explicit options:

type Status = "idle" | "loading" | "success" | "error";

const [status, setStatus] = useState<Status>("idle");

Or, as recommended in Principles for structuring state, you can group related state as an object and describe the different possibilities via object types:

type RequestState =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: any }
| { status: "error"; error: Error };

const [requestState, setRequestState] = useState<RequestState>({
status: "idle",
});

Imported State Types​

If you are using a library like React-Table, a common pattern is to import the state type from the library:

import {
PaginationState,
ColumnFiltersState,
//...
} from "@tanstack/react-table";

const DataTable: React.FC<DataTableProps> = ({ data, columns }) => {
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
);
//...
};

export default DataTable;

useCallback​

When using useCallback in a React component with TypeScript, you need to ensure that the callback function and its dependencies are correctly typed. Here are the key points and examples to consider:

Basic Usage​

import React, { useState, useCallback } from "react";

const Counter: React.FC = () => {
const [count, setCount] = useState(0);

const increment = useCallback(() => {
setCount(count + 1);
}, [count]); // The dependency array includes count

return (
<div>
<p>{count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};

export default Counter;

Typing the Callback Function​

When the callback function takes arguments, you should explicitly type the arguments and the return type if needed:

import React, { useState, useCallback } from "react";

const Multiply: React.FC = () => {
const [result, setResult] = useState(1);

const multiply = useCallback((factor: number): void => {
setResult((prevResult) => prevResult * factor);
}, []);

return (
<div>
<p>{result}</p>
<button onClick={() => multiply(2)}>Multiply by 2</button>
</div>
);
};

export default Multiply;

Complex Types​

When dealing with complex types, such as objects, you should define an interface for the types:

import React, { useState, useCallback } from "react";

interface User {
id: number;
name: string;
}

const UserList = () => {
const [users, setUsers] = useState<User[]>([]);

const addUser = useCallback((newUser: User): void => {
setUsers((prevUsers) => [...prevUsers, newUser]);
}, []);

return (
<div>
<button
onClick={() => addUser({ id: users.length + 1, name: "New User" })}
>
Add User
</button>
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
};

export default UserList;

React Built in Types​

There is quite an expansive set of types which come from the @types/react package, it is worth a read when you feel comfortable with how React and TypeScript interact. You can find them in React’s folder in DefinitelyTyped.

Here are some of the common ones.

DOM Events​

When working with DOM events in React, the type of the event can often be inferred from the event handler. However, when you want to extract a function to be passed to an event handler, you will need to explicitly set the type of the event.

import { useState } from "react";

export default function Form() {
const [value, setValue] = useState("Change me");

function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
setValue(event.currentTarget.value);
}

return (
<>
<input value={value} onChange={handleChange} />
<p>Value: {value}</p>
</>
);
}

There are many many of these pre-defined event types, you can find them in the React’s folder in DefinitelyTyped

Children​

There are two common paths to describing the children of a component. The first is to use the React.ReactNode type, which is a union of all the possible types that can be passed as children in JSX:

interface ModalRendererProps {
title: string;
children: React.ReactNode;
}

This is a very broad definition of children. The second is to use the React.ReactElement type, which is only JSX elements and not JavaScript primitives like strings or numbers:

interface ModalRendererProps {
title: string;
children: React.ReactElement;
}

Note, that you cannot use TypeScript to describe that the children are a certain type of JSX elements, so you cannot use the type-system to describe a component which only accepts <li> children.

Refs​

When working with refs in React, you can use the React.RefObject type to describe the type of the ref:

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>
);
};

Style props​

When using inline styles in React, you can use React.CSSProperties to describe the object passed to the style prop. This type is a union of all the possible CSS properties, and is a good way to ensure you are passing valid CSS properties to the style prop, and to get auto-complete in your editor.

interface MyComponentProps {
style: React.CSSProperties;
}

Automated Amazon Reports

Automatically download Amazon Seller and Advertising reports to a private database. View beautiful, on demand, exportable performance reports.

bidbear.io