React and TSX¶
This lesson covers the TypeScript additions you use when writing React with TSX: typed props, events, state, and common hooks.
JSX in TSX¶
A .tsx file allows JSX alongside TypeScript. The compiler transforms JSX into JavaScript function calls.
Typed props¶
Define an interface for component props.
interface ButtonProps {
label: string
onClick: () => void
disabled?: boolean
}
function Button({ label, onClick, disabled }: ButtonProps) {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
)
}
Children¶
interface CardProps {
title: string
children: React.ReactNode
}
function Card({ title, children }: CardProps) {
return (
<section>
<h2>{title}</h2>
{children}
</section>
)
}
UseState with explicit types¶
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState<number>(0)
return (
<button onClick={() => setCount((c) => c + 1)}>
Count: {count}
</button>
)
}
If the initial value is null or undefined, provide a type.
Event types¶
function Form() {
const [value, setValue] = useState('')
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value)
}
return <input value={value} onChange={handleChange} />
}
Generic components¶
interface ListProps<T> {
items: T[]
renderItem: (item: T) => React.ReactNode
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <ul>{items.map((item, index) => <li key={index}>{renderItem(item)}</li>)}</ul>
}
Discriminated unions in state¶
type LoadState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; message: string }
function DataView<T>({ state }: { state: LoadState<T> }) {
if (state.status === 'loading') return <p>Loading...</p>
if (state.status === 'error') return <p>{state.message}</p>
if (state.status === 'success') return <pre>{JSON.stringify(state.data)}</pre>
return <p>Ready</p>
}
Hands-on: user card¶
- Set up a React + TypeScript project with Vite:
npm create vite@latest user-cards -- --template react-ts. - Define
interface User { id: number; name: string; email: string }. - Create a
UserCardcomponent with propsuser: Userand anonSelect: (user: User) => void. - Create a
UserListcomponent that acceptsusers: User[]and renders aUserCardfor each. - Add state in
AppforselectedUser: User | nulland pass the selection handler down. - Add a form with one controlled input for
nametyped asReact.ChangeEvent<HTMLInputElement>. - Try to pass a
stringinstead ofUsertoonSelectand fix the compiler error.