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.

GroupData & Engineering
KindPattern

ℹ️ 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

urlThe 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.

💡 Key Points

💡 Tip Add a refetch function and a module level cache Map to the advanced version and you are close to the embryo of TanStack Query (see the next section).
⚠️ Warning When switching quickly the old request can come back last and overwrite the new data: you must abort it or, as in the example, drop the stale response with an alive flag.
ℹ️ Info A custom Hook must start its name with use, otherwise it may not call other Hooks legally and the lint rules will complain.
More case studies coming soon.

📚 Related Tutorials

❓ FAQ

Q Does setState after unmount warn?
A React 18 no longer warns, but the write is still wasted and may arrive out of order; keeping the abort or alive cleanup remains good practice.
Q How do I support POST?
A Pass the method and the body as arguments to useFetch(url, options), or wrap a separate mutation style Hook for write operations.
Q Do two components with the same url fire two requests?
A Yes. The state of a Hook belongs to the call site and is not shared; for a shared cache use TanStack Query or build a module level cache yourself.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏