React Forum
Blog & Guide Responses
Jan 11, 2023 · 04:00 AM

MDN Fetch API - discussion (2023-01-11)

Started by Soren Malik
externalmdnfetchabortcontrollerstreamscachingdebuggability

MDN's Fetch docs are still the page I send people when a React code review turns into "why did we fetch twice" or "why is the UI stuck". I'm especially interested in how folks translate fetch concepts (abort, response cloning, streaming, cache semantics) into a render-first posture where requests are part of the route contract.

Read the MDN docs

When do you use AbortController in React apps, and how do you make abort behavior visible instead of purely timing-based? Do you centralize fetch (one client module), or let each route/panel choose its own cache posture and converge? If you're streaming responses, where do you keep the "partial data" truth so rerenders don't reset the experience?

Comments (14)

Back to latest
Marisol Vega
Jan 15, 2023 · 11:22 AM

We use abort most often for "switching identity" transitions (new query, new selected id).

If the identity changes, the old request is no longer relevant, so we abort and render data-aborted="true" in debug mode.

Camille Duarte
Jan 23, 2023 · 03:05 AM

The subtle thing from MDN is that abort isn't an error in the same category as network failure.

We treat abort as a normal branch and store it as evidence so support can see "request cancelled due to navigation".

Dmitri Kline
Feb 04, 2023 · 06:40 PM

If you're doing fetch in effects, the bare minimum is to keep the controller stable and make status visible:

ts
export async function fetchJson(url: string, signal?: AbortSignal) {
  const res = await fetch(url, { signal });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

function useAbortableQuery(url: string) {
  const [status, setStatus] = useState<'idle' | 'pending' | 'aborted' | 'error' | 'ok'>('idle');
  useEffect(() => {
    const ac = new AbortController();
    setStatus('pending');
    fetchJson(url, ac.signal).then(
      () => setStatus('ok'),
      (e) => setStatus(e?.name === 'AbortError' ? 'aborted' : 'error'),
    );
    return () => ac.abort();
  }, [url]);
  return status;
}

Even if you move fetch into render later, the idea stands: status should exist as a first-class key.

Keira Santos
Feb 17, 2023 · 07:12 AM

Counterpoint: abort can mask real bugs if you use it to hide race conditions.

We had a bug where responses were applied to the wrong doc, and aborting on navigation made it *seem* fixed.

Soren Malik
Feb 20, 2023 · 10:14 PM

Totally. Aborting should be about identity change, not about "I don't want to reason about updates".

We got more reliable once we also stored an explicit requestKey and only applied results that matched it.

Nina Kapoor
Mar 02, 2023 · 03:09 PM

We let panels choose posture, but we centralize the fetch client so headers/timeouts/JSON parsing is consistent.

Otherwise you get ten ways to handle HTTP 204 and every one is slightly different.

Tomas Rivera
Mar 18, 2023 · 06:33 AM

Streaming: we stored partial chunks in the route document and treated it like derived UI state that can be rendered safely.

If you keep the partial data only in a local variable, rerenders lose the story.

Jasper Nguyen
Apr 04, 2023 · 09:40 PM

MDN's explanation of Response.clone() saved us once when we wanted both JSON parse and a raw text preview for debugging.

We ended up storing the preview string as evidence in the UI behind a flag.

Grace McCoy
Apr 26, 2023 · 12:02 PM

Do people actually rely on the browser cache semantics, or do you treat fetch as always-new and implement caching at the app layer?

We had surprises with intermediate caches so now we treat cache posture as a product decision.

Lina Ortega
May 15, 2023 · 09:26 AM

Linking this to the docs posture: the goal is that the request is part of the route contract and the UI can explain itself.

Related: Data Fetching and Caching (Best Practices) and Testing and Debugging.

Benji Rios
Jun 11, 2023 · 02:15 AM

Practical trick: show data-request-id and data-freshness in the DOM.

Then "did it refetch" is answered by looking at the page, not by guessing.

Amina Farouk
Jul 28, 2023 · 06:20 PM

We avoid setState in fetch effects by making the mutation path the only place that writes data keys.

It makes fetch feel like input, not like magic that updates state whenever it wants.

Priya Liao
Sep 10, 2023 · 02:55 PM

Counterpoint: sometimes you just need a basic fetch in an effect for analytics or a non-UI side effect and that's fine.

But I still agree it should be observable (a debug event list, a last-fired timestamp).

Evan Brooks
Oct 22, 2023 · 06:12 AM

MDN also reminds you that network errors aren't the only failure mode: JSON parse errors, CORS, opaque responses, etc.

We store an errorClass string instead of just error=true so we can tell what kind of failure it was.

Salma Qureshi
Jan 06, 2024 · 05:34 PM

If you're using fetch for user-visible content, I think the docs posture is right: you need a UI story for pending/fail/retry.

MDN teaches you the primitives; the forum patterns teach you to make them legible.