TypeScript
Beginner
1 min read
Typing Hooks: useState, useRef, useReducer
Example
import { useReducer, useRef, useState, type RefObject } from "react";
// useState — explicit generic when initial value is null
interface UserProfile { id: number; name: string; avatar: string }
function useProfile() {
const [profile, setProfile] = useState<UserProfile | null>(null);
const [loading, setLoading] = useState(false); // inferred: boolean
async function load(userId: number) {
setLoading(true);
const res = await fetch(`/api/users/${userId}`);
const data = (await res.json()) as UserProfile;
setProfile(data);
setLoading(false);
}
return { profile, loading, load };
}
// useRef — DOM ref (RefObject, current is readonly)
function FocusInput() {
const inputRef: RefObject<HTMLInputElement> = useRef(null);
const focusInput = () => {
inputRef.current?.focus(); // safe optional chain
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus</button>
</>
);
}
// useReducer — typed state machine
interface CartState { items: { id: number; qty: number }[]; total: number }
type CartAction =
| { type: "ADD"; id: number; price: number }
| { type: "REMOVE"; id: number; price: number }
| { type: "CLEAR" };
function cartReducer(state: CartState, action: CartAction): CartState {
switch (action.type) {
case "ADD":
return { ...state, items: [...state.items, { id: action.id, qty: 1 }], total: state.total + action.price };
case "REMOVE":
return { ...state, items: state.items.filter(i => i.id !== action.id), total: state.total - action.price };
case "CLEAR":
return { items: [], total: 0 };
}
}
function Cart() {
const [cart, dispatch] = useReducer(cartReducer, { items: [], total: 0 });
return <div>Items: {cart.items.length}, Total: ${cart.total}</div>;
}
Related Resources
TypeScript Reference
Complete tag & property list
TypeScript How-To Guides
Step-by-step practical guides
TypeScript Exercises
Practice what you've learned
More in TypeScript