useFetch Pattern The useFetch Custom Hook
Last updated: 2026-11-02
useFetch Pattern The useFetch Custom Hook
Extract the three-state request logic into a useFetch(url) custom Hook: a component calls one line and gets { data, loading, error }, so the request state no longer scatters around the codebase. This is the classic case of reusing logic, and the stepping stone to TanStack Query. Every component shares the same well-tested behaviour, and the cleanup stays in one place instead of being copied around.
| Group | Data & Engineering |
|---|---|
| Kind | Pattern |
ℹ️ Info The editor transpiles JSX to JavaScript and renders the real UI in a sandbox; components mount automatically — no manual render call needed.
📝 Syntax
function useFetch(url) {
const [state, setState] = useState({ data: null, loading: true, error: null })
useEffect(() => {
const ctrl = new AbortController() // cancel the old request when url changes or on unmount
setState({ data: null, loading: true, error: null })
fetch(url, { signal: ctrl.signal })
.then(r => r.ok ? r.json() : Promise.reject(new Error('HTTP ' + r.status)))
.then(data => setState({ data, loading: false, error: null }))
.catch(err => err.name !== 'AbortError' &&
setState({ data: null, loading: false, error: err }))
return () => ctrl.abort() // cleanup: stops a stale response from overwriting fresh data
}, [url])
return state
}
⚙️ Parameters
| url | The request address; it acts as a dependency, so when the url changes the Hook refetches automatically and cancels the previous request. |
|---|
Returns:No return value; this is a coding pattern rather than a callable API.
▶ Example
The example runs out of the box: it auto-renders when expanded, and edits take effect instantly.
Output:
Clicking "Switch to #2" fires a new request, shows the loading state for a moment and then renders the matching todo - the component writes a single useFetch(url) line while the whole three-state logic is reused.