Commit 9e44db4
committed
Five hooks keyed a `useCallback`/`useEffect` on values the caller supplies
inline — `where`/`fields`/`orderBy` objects, `onSuccess`/`onError` handlers, and
the `fetcher` `useMetadata` takes as a required positional argument. Inline
means a fresh identity every render, so the effect re-ran every render; because
the fetch hooks call `setState`, that render caused another. Under the hooks'
own documented usage this was an unbounded request loop.
Requests issued in 250ms by one mounted component, before → after:
useQuery (inline where) 4691 → 1
useInfiniteQuery (inline where) 6611 → 1
useObject (NO options at all) 4306 → 1
useView (inline onSuccess) 8197 → 1
useMetadata (inline fetcher) 7654 → 1
useObject and useMetadata needed no particular usage to loop: the former
depended on its own `data`/`etag` state while writing both, the latter takes its
fetcher positionally so there is no non-inline way to call it. useMutation was
never affected — no effect drives it.
The same root cause churned the realtime subscriptions (#4694): useAutoRefresh
with an unmemoized `refetch` — which is exactly what useQuery returned every
render — resubscribed on both streams every render, losing any event delivered
in the unsubscribe/resubscribe gap.
Adds two internal primitives (not exported): `stableKey` derives a dependency
from a structural VALUE (sorted keys, array order preserved, since `orderBy` is
positional) so a rebuilt-but-equal object is a no-op; `useEventCallback` gives a
handler a fixed identity while always invoking its latest version, synced in an
effect rather than during render so a discarded concurrent render cannot publish
a handler that never committed.
Fixing this by asking callers to memoize was rejected: the TSDoc examples
themselves pass object literals, and correctness must not rest on every call
site remembering `useMemo`.
13 tests, each verified by reverting the fix it guards — restoring identity deps
in useQuery (3 red), useObject's self-referential state (3), useMetadata's
fetcher dep (2) and the subscription callback dep (2). Counts are asserted
exactly rather than as an upper bound, which would pass on a loop that merely
got slower. Coverage includes the inverse direction: a changed value still
refetches, and every stabilized handler runs its newest version, so the ref
indirection cannot silently trade a loop for a stale closure.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYnZrTwXbrctB8E8HpJAPT
1 parent 0076683 commit 9e44db4
6 files changed
Lines changed: 586 additions & 37 deletions
File tree
- .changeset
- packages/client-react/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
| |||
113 | 114 | | |
114 | 115 | | |
115 | 116 | | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
116 | 137 | | |
117 | 138 | | |
118 | 139 | | |
| |||
141 | 162 | | |
142 | 163 | | |
143 | 164 | | |
144 | | - | |
| 165 | + | |
145 | 166 | | |
146 | 167 | | |
147 | 168 | | |
148 | | - | |
| 169 | + | |
149 | 170 | | |
150 | 171 | | |
151 | 172 | | |
152 | 173 | | |
153 | | - | |
| 174 | + | |
154 | 175 | | |
155 | 176 | | |
156 | 177 | | |
| |||
520 | 541 | | |
521 | 542 | | |
522 | 543 | | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
523 | 556 | | |
524 | 557 | | |
525 | 558 | | |
| |||
564 | 597 | | |
565 | 598 | | |
566 | 599 | | |
567 | | - | |
| 600 | + | |
568 | 601 | | |
569 | 602 | | |
570 | 603 | | |
571 | | - | |
| 604 | + | |
572 | 605 | | |
573 | 606 | | |
574 | 607 | | |
575 | 608 | | |
576 | | - | |
| 609 | + | |
577 | 610 | | |
578 | 611 | | |
579 | 612 | | |
| |||
0 commit comments