React & TypeScript Cheatsheet
This React & TypeScript Cheatsheet is a quick reference for using TypeScript in a React project. It covers essential aspects like declaring types, interfaces, and using generics in React components and using TypeScript with React hooks.
Table of Contents
- Basic Setup with Vite
- Types and Interfaces
- React Components
- React Component Return Types
- React Hooks
Basic Setup with Vite
First, create a new React project with TypeScript support using Vite:
npm init vite my-app
When prompted, select the framework React and the variant TypeScript. Vite will install types for React, so you don’t need to install additional type packages.
Types and Interfaces
When to use type
vs interface
In TypeScript, both type
and interface
can define the shape of an object. In most cases either a type
or an interface
could be used and the choice usually comes down to personal preference or convention within a codebase.
Use interface
to define the shape of your props:
interface AppProps {
title: string
isLoading: boolean
}
const App: React.FC<AppProps> = ({ title, isLoading }) => {
// ...
}
You can also use the type
keyword:
type AppProps = {
title: string
isLoading: boolean
}
const App: React.FC<AppProps> = ({ title, isLoading }) => {
// ...
}
React Components
Function Component
import React from 'react'
interface Props {
message: string
}
const MyComponent: React.FC<Props> = ({ message }) => {
return <div>{message}</div>
}
export default MyComponent
Class Component
import React, { Component } from 'react'
interface Props {
message: string
}
interface State {
count: number
}
class MyComponent extends Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = {
count: 0,
}
}
render() {
return (
<div>
<div>{this.props.message}</div>
<div>{this.state.count}</div>
</div>
)
}
}
export default MyComponent
React Component Return Types
Function Components
For function components, use the React.FC
(or React.FunctionComponent
) type, which includes the return type and prop types:
interface Props {
message: string
}
const MyComponent: React.FC<Props> = ({ message }) => {
return <div>{message}</div>
}
Alternatively, you can define the return type explicitly using React.ReactElement
or JSX.Element
:
const MyComponent = ({ message }: Props): React.ReactElement => {
return <div>{message}</div>
}
const MyComponent = ({ message }: Props): JSX.Element => {
return <div>{message}</div>
}
Both React.ReactElement
and JSX.Element
can be used as return types for React components, and they’re often interchangeable.
If you want some general guidelines to follow:
- Use
React.ReactElement
when you want to be explicit about the dependency on the React library, or if you’re working with React-specific APIs or features. - Use
JSX.Element
when you want a more generic return type that represents a JSX element, which could be useful if you’re writing code that is meant to be library-agnostic or reusable across different JSX-based libraries.
Class Components
For class components, extend the React.Component
class with the prop types and state types, and define the return type for the render method using React.ReactNode
:
import React, { Component } from 'react'
interface Props {
message: string
}
interface State {
count: number
}
class MyComponent extends Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = {
count: 0,
}
}
render(): React.ReactNode {
return (
<div>
<div>{this.props.message}</div>
<div>{this.state.count}</div>
</div>
)
}
}
export default MyComponent
React Hooks
useState
import React, { useState } from 'react'
interface Props {
initialCount: number
}
const Counter: React.FC<Props> = ({ initialCount }) => {
const [count, setCount] = useState<number>(initialCount)
return (
<div>
<button onClick={() => setCount(count - 1)}>-</button>
<span>{count}</span>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
)
}
useReducer
In this example, we will type a useReducer
hook for a simple counter application.
First, define the action types and the state interface:
// Define the action types as a TypeScript enum
enum ActionType {
Increment,
Decrement,
Reset,
}
// Define the state interface
interface State {
count: number
}
Next, define the reducer function with typed action and state:
interface Action {
type: ActionType
payload?: any
}
const counterReducer = (state: State, action: Action): State => {
switch (action.type) {
case ActionType.Increment:
return { count: state.count + 1 }
case ActionType.Decrement:
return { count: state.count - 1 }
case ActionType.Reset:
return { count: 0 }
default:
return state
}
}
Finally, create a component that uses the useReducer
hook with the typed reducer function:
import React, { useReducer } from 'react'
const Counter: React.FC = () => {
const initialState: State = { count: 0 }
const [state, dispatch] = useReducer(counterReducer, initialState)
return (
<div>
<button onClick={() => dispatch({ type: ActionType.Decrement })}>
-
</button>
<span>{state.count}</span>
<button onClick={() => dispatch({ type: ActionType.Increment })}>
+
</button>
<button onClick={() => dispatch({ type: ActionType.Reset })}>
Reset
</button>
</div>
)
}
export default Counter
useRef
import React, { useRef } from 'react'
const TextInputWithFocusButton: React.FC = () => {
const inputEl = useRef<HTMLInputElement>(null)
const onButtonClick = () => {
// `current` points to the mounted text input element
if (inputEl.current) {
inputEl.current.focus()
}
}
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
)
}