Custom hooks with generics & inference
Write reusable custom hooks with for values, results, and errors; leverage inference and constraints for safe APIs.
Intro
Goal: Build generic hooks that feel native: a typed localStorage hook, an async loader, and a controlled/uncontrolled helper. You will rely on inference so callers rarely annotate types.
- Return tuples for React-like ergonomics
- Use constraints when needed
- Keep no any
useLocalStorage<T>
Return a [value, set] tuple like useState. Keep T consistent across value and setter.
import { useEffect, useState } from "react"
export function useLocalStorage<T>(key: string, initial: T): [T, (v: T | ((prev: T) => T)) => void] {
const [value, setValue] = useState<T>(() => {
try {
const raw = localStorage.getItem(key)
return raw ? (JSON.parse(raw) as T) : initial
} catch {
return initial
}
})
useEffect(() => {
try { localStorage.setItem(key, JSON.stringify(value)) } catch {}
}, [key, value])
const set = (v: T | ((prev: T) => T)) => {
setValue(prev => (typeof v === "function" ? (v as (p: T) => T)(prev) : v))
}
return [value, set]
}
// Usage
// const [user, setUser] = useLocalStorage("user", { id: 1, name: "Ada" })All lessons in this course
- Custom hooks with generics & inference
- Suspense & error boundaries types