Recipes
Use with React
Use this when a component loads data in useEffect without a query library. If the component unmounts, or its id changes, before the answer arrives, the old call still finishes, and without a cleanup it writes its answer into state.
Cancel the call in the effect’s cleanup, and skip the 'abort' it ends with. This hook uses React’s useEffect and useState, imported from react. api and User are from Quick start.
type UserState = { id?: string; user?: User; failed?: boolean }
function useUser(id: string): UserState {
const [state, setState] = useState<UserState>({})
useEffect(() => {
const controller = new AbortController()
api.getUser({ id }, { signal: controller.signal }).then(({ data, error }) => {
if (error?.kind === 'abort') return // unmounted, or id changed
setState(error ? { id, failed: true } : { id, user: data })
})
return () => controller.abort() // cancel when the component goes away
}, [id])
// The answer for an earlier id isn't this id's: loading until it arrives.
return state.id === id ? state : {}
}
A component reads the three states the hook returns:
function Profile({ id }: { id: string }) {
const { user, failed } = useUser(id)
if (failed) return <p>Couldn't load the profile.</p>
if (!user) return <p>Loading…</p>
return <h1>{user.name}</h1>
}
Why it works
React runs an effect’s cleanup before it runs the effect again with a new id, and once more when the component unmounts. The cleanup aborts that run’s controller, so its call ends with kind: 'abort', and the .then returns before setState. Only the call for the current id can write to state, so a fast navigation never writes stale data into a component that has moved on.
- In development, Strict Mode runs one extra setup and cleanup before the first real setup (
useEffect). The first call is cancelled and skipped like any other, and the second one loads the user. - A cancelled call isn’t reported to
onError, so leaving a page never reaches your error tracker. - A new
idshows loading, not the previous user. Each answer is stored with theidit is for, and the hook returns{}until the currentid’s answer arrives. - Every other failure sets
{ failed: true }. Keeperrorin state instead if the component needs to tell a 404 from being offline (Handling errors).
For caching, refetching and sharing data between components, use TanStack Query with liaise. TanStack aborts the query’s signal when the query is no longer needed, so you don’t write the cleanup yourself.
Next: Load the current user into a store stops several components from each asking for the same user.