Community JavaScript Snippet
Batch and Coalesce Fetch Calls in React
Twenty cards on a page each ask for /api/users/:id and you cut server load 20x with this 30-line dataloader. The batch fires on the next microtask; same id never goes over the wire twice.
Batch and Coalesce Fetch Calls in React
Twenty cards on a page each ask for /api/users/:id and you cut server load 20x with this 30-line dataloader. The batch fires on the next microtask; same id never goes over the wire twice.
By @sophiesharma
March 25, 2026
·
Updated May 18, 2026
813 views
22
4.4 (10)
Three moving parts: a pending queue, a one-shot scheduled flag, and queueMicrotask to fire the flush at the end of the current synchronous burst. The microtask boundary is the right granularity for React: between the time the first component's effect runs and the next paint, every component on the page has had a chance to enqueue its id. The new Set dedupe before the batch fetch is the second win after batching itself; the same user id requested by 5 cards still hits the API once. I have shipped this exact pattern to coalesce avatar lookups across a discussion thread; it cut requests by 12x without any component changes.
The integration with React is straightforward but easy to get wrong. The loader instance MUST be scoped to a single 'request' (a render pass for client React, a single HTTP request for SSR), not memoized across the app, or you accumulate stale entries forever. In the client I create one with useMemo(() => createLoader(...), []) at the page-component level; in Next.js server components I pass the loader through React context for the duration of one render. I deliberately do not cache the resolved values inside createLoader itself; combine it with memoizeAsync from the previous snippet if you want the user-cached-across-batches behavior. Mixing the two responsibilities into one helper is what bloats the abstraction.
Real services have hard limits: AWS API Gateway caps query strings at 8KB, Stripe's batch endpoint is 100 entries per request, our internal users-by-ids endpoint refused anything over 50. The chunked flush respects whatever ceiling you set; the only reason it stays simple is that all chunks share the same pending slot, so subsequent batches still serialize correctly. A subtler trade-off: if maxBatchSize is set too low, you defeat the entire batching benefit. I usually set it just under the server's hard cap, so the batch scales up to that ceiling and then spills cleanly.
