Deck 17 - Data Fetching + Caching Patterns Flashcards

React Query/SWR patterns: query keys, enabled guards, invalidation, optimistic updates + rollback, retries, pagination, cache scoping, and edge-case review wording. (80 cards)

1
Q

Deck 17 - Data Fetching + Caching Patterns (React Query/SWR style) (objective)

A

Goal: review async data code like a senior: caching, invalidation, query keys, optimistic updates, retries, pagination, and edge cases.
You learn: what/why (under the hood), common bugs, and evaluator-grade review wording.
Output style: PASS/PARTIAL/FAIL + 2-3 issues + fix approach.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Mental model: server state vs client state (again, in caching terms).

A

Server state is remote and can be stale; it needs caching, invalidation, and refetch.
Client state is local and authoritative (UI toggles, selections).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Mental model: cache key is the identity of data.

A

Query keys define what data is cached.
If the key is wrong/unstable, you get collisions, refetch storms, or stale data.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Mental model: stale vs fresh vs invalidated.

A

Fresh: within staleTime. Stale: eligible to refetch. Invalidated: marked stale now.
Stale does not mean wrong; it means revalidation may run.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Mental model: deduping and request coalescing.

A

Libraries dedupe in-flight requests for same key.
Without it, components can trigger duplicate network calls.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Mental model: retries are not always safe.

A

Retrying POST/PUT can duplicate writes unless idempotent.
Prefer retries for GET; for mutations, ensure idempotency or disable retries.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Mental model: optimistic update is a temporary local illusion.

A

You update cache immediately, then confirm or roll back.
Requires rollback logic and careful invalidation on settle.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Mental model: ‘keep previous data’ vs flicker.

A

Keeping previous data avoids UI flicker during refetch.
But can show stale content; use loading indicators or ‘updating’ state.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Mental model: background refetch triggers.

A

Focus/reconnect/refetchInterval can refetch unexpectedly.
Reviewer: ensure it is intentional and not causing rate limits.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Mental model: pagination and cache shape.

A

Paginated data can be cached per page or as an infinite list.
Wrong key/merge strategy can duplicate items or break ordering.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Mental model: normalization vs caching per query.

A

React Query caches per query response.
Normalization can help large graphs but increases complexity; use when needed.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Mental model: cancellation and race protection.

A

Requests should be cancellable (AbortController) or at least guarded against stale responses.
Libraries handle some races; manual fetch needs explicit guard.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Mental model: error states are part of UX contract.

A

Handle loading/error/empty states consistently.
Do not treat errors as just console logs.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Mental model: validation at the boundary.

A

Treat API responses as unknown; validate (Zod) at boundary.
Prevents runtime crashes from unexpected shapes.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Mental model: cache invalidation is a correctness problem.

A

If you forget invalidation, UI shows stale data.
If you over-invalidate, you cause refetch storms.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Mental model: queryFn purity.

A

Query functions should be deterministic for a given key.
They should not depend on unstable closures or mutable globals.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Mental model: suspense vs manual loading.

A

Suspense centralizes loading handling.
But requires boundaries and can complicate error handling; match approach to app needs.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

Mental model: auth tokens and caching.

A

Cache may contain user-specific data.
On logout/login, clear caches or scope keys by user to avoid leaking data across sessions.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

Mental model: prefetching improves perceived performance.

A

Prefetch likely next data (hover, route transition).
But avoid wasteful prefetch that spikes bandwidth.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

Mental model: selecting and transforming data.

A

Use ‘select’ to derive view models in query layer.
Keeps UI components simpler and avoids repeated transforms.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

Pattern: stable query keys (rule).

A

Keys should be arrays with stable primitives.
Example: [‘user’, userId] not [‘user’, { id: userId }] unless object is stable/serialized.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

Pattern: include all params in key.

A

Search/sort/page/filter params must be part of key.
Otherwise cached results can be returned for wrong inputs.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

Pattern: use enabled for conditional queries.

A

If a query depends on an ID, set enabled: !!id.
Prevents fetching with undefined and reduces error noise.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

Pattern: staleTime vs cacheTime (concept).

A

staleTime controls refetch eligibility; cacheTime controls how long unused data stays in memory.
Tune to UX and data volatility.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Pattern: invalidate on mutation success.
After mutation, invalidate related queries (or update cache directly). Pick minimal invalidation to avoid unnecessary refetches.
26
Pattern: optimistic update with rollback (steps).
1) cancel queries 2) snapshot previous 3) update cache 4) rollback on error 5) invalidate on settle.
27
Pattern: avoid retrying non-idempotent mutations.
Retries can create duplicates. If mutation is idempotent, include idempotency key and document it.
28
Pattern: keepPreviousData for pagination.
Avoid flicker when switching pages. Show an 'updating' indicator to communicate staleness.
29
Pattern: debounce user input queries.
Debounce search input to reduce request storms. Use stable debounced value as key param.
30
Pattern: abort/cancel in-flight when changing params.
Libraries often cancel on new fetch; ensure underlying fetch respects abort. For custom fetch: pass AbortController signal.
31
Pattern: handle empty state separately from error.
Empty results is a valid state; errors are failures. Provide distinct UI messages.
32
Pattern: schema validation at boundary (Zod).
Parse unknown JSON into typed models. On validation failure: treat as error and log safely.
33
Pattern: select to compute view models.
Use select to transform data once at cache boundary. Reduces repeated mapping in UI and improves consistency.
34
Pattern: cache scoping by user.
Include userId in query keys or clear cache on auth changes. Prevents showing prior user's data.
35
Pattern: prefetch on navigation intent.
Prefetch route data on hover or when link becomes visible. Reduces perceived latency.
36
Pattern: refetchOnWindowFocus tradeoff.
Improves freshness but can annoy users and stress APIs. Disable or tune if data is not volatile.
37
Pattern: error retries with backoff.
Use exponential backoff for transient failures. Cap retries to avoid infinite loops.
38
Pattern: infinite queries (high level).
Use cursor-based pagination when possible. Merge pages carefully and dedupe items by stable IDs.
39
Pattern: dedupe and normalize list updates (practical).
When merging pages or optimistic updates, dedupe by id. Prevents duplicates and broken keys.
40
Pattern: consistent loading UI strategy.
Use skeletons for initial load; subtle 'updating' indicator for background refetch. Avoid full-page spinners for minor refetch.
41
Gotcha: unstable query key object. ``` useQuery({ queryKey: ['user', { id }], queryFn: () => fetchUser(id) }) ```
PARTIAL - Object in key can be unstable. Use primitives or stable serialization. Prefer ['user', id].
42
Gotcha: missing params in key. ``` useQuery({ queryKey: ['search'], queryFn: () => search(q) }) ```
FAIL - Cache collision: different q shares same key. Include q in key: ['search', q].
43
Gotcha: fetch runs with undefined id. ``` useQuery({ queryKey: ['user', id], queryFn: () => fetchUser(id) }) ```
FAIL - If id can be undefined, queryFn may crash or hit wrong endpoint. Use enabled: !!id and guard.
44
Gotcha: mutation does not invalidate cache.
FAIL - UI can remain stale after mutation. Invalidate related queries or update cache directly on success.
45
Gotcha: optimistic update without rollback.
FAIL - If mutation fails, UI stays wrong. Snapshot previous cache and rollback on error.
46
Gotcha: retrying non-idempotent mutation.
FAIL - Retries can duplicate writes. Disable retry or implement idempotency keys.
47
Gotcha: queryFn uses unstable closure. ``` const fn = () => fetch(`/api?q=${q}`); useQuery({ queryKey: ['search', q], queryFn: fn }) ```
PARTIAL - It's okay if q is in key, but ensure queryFn uses key param or stable dependencies to avoid confusion. Prefer queryFn receives key params.
48
Gotcha: manual fetch in useEffect duplicates library cache.
PARTIAL - Two sources of truth (manual state + query cache) drift. Prefer a single server-state approach (React Query/SWR).
49
Gotcha: over-invalidation (refetch storm).
PARTIAL - Invalidating broad keys causes excessive refetches. Invalidate the smallest key set needed.
50
Gotcha: cache not cleared on logout.
FAIL - Can leak prior user's data. Clear query cache on logout or scope keys by user/session.
51
Gotcha: stale UI without 'updating' signal.
PARTIAL - keepPreviousData may show old page results. Provide subtle updating indicator and disable actions if needed.
52
Gotcha: using index keys after pagination merge.
FAIL - Duplicates/reordering can break list identity. Use stable ids and dedupe merges.
53
Gotcha: no error boundary for query errors (UX).
PARTIAL - Ensure errors are handled and shown consistently. Use error UI and retries where appropriate.
54
Gotcha: parsing JSON without validation at boundary.
FAIL - Unknown data shape can crash UI. Validate with schema and treat invalid response as error.
55
Gotcha: refetch on focus causing rate limits.
PARTIAL - Might hammer APIs. Tune refetchOnWindowFocus and staleTime, especially for expensive endpoints.
56
Mini-drill: reviewer wording for missing query key params.
FAIL - Query key does not include input params, causing cache collisions and incorrect results. Include params in key (e.g., ['search', q]).
57
Mini-drill: reviewer wording for missing invalidation.
FAIL - Mutation updates server but cache is not updated or invalidated, leaving UI stale. Invalidate affected queries or update cache on success.
58
Mini-drill: reviewer wording for optimistic update rollback.
FAIL - Optimistic update lacks rollback path on error. Snapshot previous cache and restore on failure; invalidate on settled.
59
Mini-drill: reviewer wording for logout cache leak.
FAIL - Server cache can persist across sessions, showing prior user's data. Clear query cache on logout or scope keys by user/session.
60
Mini-drill: reviewer wording for over-invalidation.
PARTIAL - Invalidation is overly broad and can trigger refetch storms. Narrow invalidation to the smallest affected query keys.
61
Mini-drill: reviewer wording for retries on mutation.
FAIL - Retrying non-idempotent mutations can duplicate writes. Disable retries or implement idempotency and document it.
62
Mini-drill: checklist for query key correctness.
1) Stable primitives 2) Includes all inputs 3) Scoped by user/session when needed 4) No collisions across features 5) Matches queryFn behavior
63
Mini-drill: when to prefetch.
Prefetch when probability of navigation is high (hover/near viewport) and data is reusable. Avoid prefetching large/rarely used data.
64
Mini-drill: when to use keepPreviousData.
Use for pagination/filter changes to prevent flicker, but provide an 'updating' indicator and keep keys correct to avoid misleading UI.
65
Mini-drill: difference between staleTime and cacheTime.
staleTime: how long data is considered fresh before refetch. cacheTime: how long unused data stays before garbage collected.
66
Checklist: server state review top 10.
1) Correct query keys 2) enabled guards 3) Cancellation/race protection 4) Clear loading/error/empty states 5) Validation at boundary 6) Invalidation/update strategy 7) Optimistic update rollback 8) Retry policy correctness 9) Pagination merge/dedupe 10) Auth/session cache scoping
67
Example: stable key + enabled. ``` useQuery({ queryKey: ['user', userId], enabled: !!userId, queryFn: () => fetchUser(userId!), }); ```
PASS - Key includes id and query is disabled until id exists.
68
Example: optimistic update skeleton (high level). ``` onMutate: async (vars) => { await qc.cancelQueries({ queryKey: ['items'] }); const prev = qc.getQueryData(['items']); qc.setQueryData(['items'], (old) => optimistic(old, vars)); return { prev }; }, onError: (_err, _vars, ctx) => { qc.setQueryData(['items'], ctx?.prev); }, onSettled: () => { qc.invalidateQueries({ queryKey: ['items'] }); } ```
PASS - Cancel + snapshot + update + rollback + invalidate on settled.
69
Example: pagination key includes page params. ``` useQuery({ queryKey: ['search', q, page, sort], queryFn: () => search({ q, page, sort }), keepPreviousData: true, }); ```
PASS - Cache correctness for different pages/sorts; avoids flicker.
70
Example: validate response at boundary. ``` const Data = Schema.parse(await res.json()); return Data; ```
PASS - Treats API response as unknown and validates before UI uses it.
71
Anti-pattern: local state copy of query data (why).
Copying query data into useState causes drift and extra effects. Prefer using query data directly or use select for derived values.
72
Anti-pattern: key mismatch with queryFn.
If queryFn reads q but key omits q, cache serves wrong results. Rule: queryFn must be a function of the key inputs.
73
Anti-pattern: invalidating all queries on any mutation.
Overly broad invalidation triggers refetch storms. Invalidate only the affected keys or update cache precisely.
74
Mini-drill: write a one-sentence verdict for cache leak.
FAIL - Cached server data is not scoped or cleared on auth changes, risking cross-user data exposure.
75
Mini-drill: write a one-sentence verdict for key collision.
FAIL - Query key does not include required parameters, causing cache collisions and incorrect results across different inputs.
76
Mini-drill: what edge cases to test for pagination.
Empty page, last page boundary, rapid page changes, dedupe across pages, error on next page, and preserving selection across page changes.
77
Mini-drill: what edge cases to test for optimistic updates.
Server error rollback, partial failures, concurrent mutations, out-of-order responses, and reconciliation after invalidation.
78
Mini-drill: how to reduce request storms.
Debounce inputs, use staleTime, disable refetch on focus if not needed, narrow invalidations, and dedupe in-flight queries.
79
Mini-drill: reviewer wording for validation.
FAIL - API response is assumed to match expected shape. Validate at the boundary (schema) and handle invalid responses as errors.
80
Mini-drill: reviewer wording for retry policy.
PARTIAL - Retry policy is unsafe for non-idempotent mutations. Disable retries or ensure idempotency; keep retries for safe GETs.