React
medium
very high
mid
Build a custom hook that handles loading, error, and success states
A correct fetch hook tracks {status, data, error}, cancels in-flight requests on unmount or arg change with AbortController, and avoids stale-state-after-unmount warnings. The reducer pattern keeps transitions safe.
7 min read·~25 min to think through
The "naïve useFetch" interview answer ships three classic bugs: it sets state after unmount, it doesn't cancel the previous request when the URL changes, and it spreads three booleans (loading, error, data) that can disagree (e.g. loading=true while data is also set).
A solid hook fixes all three:
- Single state machine via
useReducerso transitions are atomic. - AbortController scoped to each effect, aborted in cleanup.
- Effect deps include the URL (and a
refreshKeyif you want manual refresh).
Senior nuance: in production you'd use React Query / SWR instead — they add caching, deduping, retries, focus revalidation, and SSR hydration. Build this hook to demonstrate the mechanics; recommend the library for actual code.
Code
Follow-up questions
- •How does this compare to React Query? What does it not handle?
- •How do you add retry-with-exponential-backoff?
- •How would you extend this to support optimistic updates?
Common mistakes
- •Three booleans (loading/error/data) instead of a tagged union — they disagree.
- •Forgetting AbortController — the previous request can resolve after the new one and overwrite fresh data.
- •Setting state after unmount and chasing the React warning instead of cleaning up.
Performance considerations
- •Without dedupe, two components fetching the same URL fire two requests — React Query / SWR de-dupe by key.
Edge cases
- •AbortError is an expected error — must be filtered, not surfaced to the UI.
- •Strict Mode double-invokes the effect in dev; ensure cleanup truly aborts.
Real-world examples
- •TanStack Query implements this same state machine plus cache, dedupe, refocus revalidation, and SSR hydration.
Senior engineer discussion
Senior signal: discuss why a custom hook is rarely the right answer in production, what 'data fetching primitives' libraries solve (cache key, structural sharing, retry, focus, mutate), and the new use() + Suspense data-fetching model.
Related questions
React
Medium
hot
6 min