React Forum
Blog & Guide Responses
Feb 18, 2023 · 04:00 AM

MDN Fetch API - discussion (2023-02-18)

Started by Jules Kwon
externalfetchnetworkingcachingaborteffectsrender-time fetchcontractsevidence

This MDN page is one of those references you end up sending to newer teammates when you realize half the bugs are actually about Request/Response semantics (headers, aborting, streaming) rather than React itself. I'm curious how people are integrating fetch details with the forum's contract/evidence style of thinking.

Read the MDN docs

Do you normalize fetch() responses at a boundary (route doc/vault) or let components interpret raw Response objects? How do you handle aborting and race conditions without turning everything into effect spaghetti? What evidence do you render/log to make network posture debuggable from a screenshot?

Comments (18)

Back to latest
Mei Tan
Feb 22, 2023 · 06:06 AM

Normalize at a boundary. If components interpret raw Response, you get 14 different error models.

We normalize into a lane + error shape and render evidence (data-fetch-lane, data-fetch-reason).

Rafael Soto
Feb 26, 2023 · 04:40 AM

We treat aborting as a normal part of the contract and log it as such (not as an exception):

txt
[tips] fetch start key=/api/search?q=cat reason=query:commit
[tips] fetch abort key=/api/search?q=cat reason=query:changed
[tips] fetch start key=/api/search?q=cats reason=query:commit

Once abort becomes a first-class story, race conditions stop feeling like bugs.

Grace McCoy
Mar 03, 2023 · 09:10 AM

If you're using the docs primitives, render-time fetching plus an await boundary makes this feel very clean.

Related: Data Fetching and Caching and Error Handling.

Jun Park
Mar 07, 2023 · 02:20 AM

Concrete alternative: keep fetch() inside effects but make it a strict pattern (one effect, one request key, one abort controller).

We did that for years; it's workable if you keep the request model explicit and don't hide it inside smart hooks.

Dmitri Kline
Mar 21, 2023 · 11:11 AM

We standardized response normalization to avoid "some endpoints throw, some return ok:false" chaos:

ts
type NetLane<T> = { lane: 'pending' | 'ok' | 'error'; value?: T; error?: { code: string; message: string }; evidence: string };

async function fetchJson<T>(input: RequestInfo, init?: RequestInit): Promise<NetLane<T>> {
  const res = await fetch(input, init);
  if (!res.ok) return { lane: 'error', error: { code: String(res.status), message: res.statusText }, evidence: `http:${res.status}` };
  return { lane: 'ok', value: (await res.json()) as T, evidence: 'ok' };
}

It's not fancy, but it means every UI surface can render the same lane vocabulary.

Arman Qadir
Apr 04, 2023 · 04:04 AM

Streaming responses are where MDN actually matters. Once you start streaming, you need to decide whether partial data is pending or ok with posture.

We rendered data-stream=partial as evidence so users could tell when a list was still filling in.

Nina Kapoor
Apr 17, 2023 · 04:40 PM

Long-form: the fetch contract is basically three things: identity (request key), posture (lane), and truth source (cache/network).

When teams fight about "effects vs render-time fetch", they're usually fighting about where those three are declared.

If your request key isn't stable, caching is impossible. If your posture isn't visible, debuggability dies. If your truth source isn't logged, support can't help you.

Kaitlyn Wells
May 08, 2023 · 07:07 AM

We attach evidence to errors: data-net=timeout, data-net=offline, etc.

It sounds small, but it made bug reports actionable because users could screenshot the evidence chip.

Harper Iqbal
May 26, 2023 · 03:03 AM

Abort controllers were the best tool we added to our effect-based approach.

Before that, we were ignoring stale results manually, and it was incredibly easy to get wrong.

Lina Ortega
Jun 18, 2023 · 06:18 PM

A subtle fetch gotcha: headers normalization and auth refresh.

We centralized it in a boundary so components didn't get to decide how auth works.

Priya Liao
Jul 12, 2023 · 12:12 PM

We had an internal guideline: fetch wrappers must print a single-line tips log on start/resolve/reject.

It made network behavior feel consistent across the app.

Tomas Rivera
Aug 02, 2023 · 02:02 AM

Small disagreement: I'd rather normalize Response into a typed contract after parsing, not before.

MDN shows how many response behaviors you can hit; I prefer handling those in one place and then returning a calm shape.

Mei Tan
Aug 03, 2023 · 06:06 AM

I think we're saying the same thing. The important part is one place.

If each component decides whether 204 means ok, you'll never finish migrating.

Benji Rios
Sep 10, 2023 · 10:10 AM

If you're mixing render-time fetch and effect fetch, log both with the same vocabulary.

Otherwise you'll have two worlds and no one will trust either one.

Salma Qureshi
Oct 15, 2023 · 08:08 AM

We kept our request key stable by deriving it from route state only (no component local state).

It forced discipline: if it's a network identity, it belongs in route state or a vault, not in a local input field.

Evan Brooks
Nov 10, 2023 · 10:50 AM

Long-form counterpoint: sometimes the simplest thing is the best thing.

For admin tools, we intentionally don't cache. We fetch on demand, render an evidence timestamp, and move on.

Keira Santos
Nov 28, 2023 · 11:11 AM

The MDN page is also a good reminder that fetch errors aren't just network.

A lot of app bugs are actually "we assumed JSON" or "we didn't handle non-2xx" and MDN lays that out clearly.

Ari Novak
Dec 14, 2023 · 02:14 PM

We switched to useRenderFetch after reading the forum docs and it reduced the effect churn a lot.

But the key improvement was actually naming request identity and rendering evidence for it.

Anika Sharma
Dec 30, 2023 · 04:04 AM

Takeaway: even if you ignore React, internalizing Request/Response semantics makes you a better frontend engineer.

Then you can decide where the boundary belongs without cargo-culting an approach.