You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
finding(react): useETagCache re-allocates its config object on every render inside useRef #6817
Noticed while implementing objectui#6797 (the react-hooks/refs write on this
same hook). Not repaired there: it is a different defect class, nothing in that
card's gate family covers it, and the repair there did not require touching this
line.
What
packages/react/src/hooks/useETagCache.ts:211 (measured on a77a00c2c):
useRef's argument is evaluated on every render, and every result after the
first is discarded — useRef only reads it to seed the initial value. So this
allocates a fresh five-key object per render of every component using the hook,
forever, to no effect.
Why it is worth a card rather than a shrug
It is the same pattern PR objectui#6796 named and repaired in useSchemaPersistence — useRef(createLocalStorageAdapter()), which invoked a
factory on every render and kept only the first result. That one had to change
because clearing its warning required it; this one does not, which is exactly
why it survives and why it needs its own card rather than a rider.
The cost here is far smaller than objectui#6796's case: an object literal, not a
factory call, and the discarded object holds no resources. No user-visible
impact is claimed — this is an observation, filed for triage to grade.
Not measured
Whether any consumer renders this hook hot enough for the allocation to matter. useETagCache currently has zero in-repo consumers (packages/react/src/hooks/index.ts:35
exports it; nothing else in the repo calls it), so any impact is on published
consumers only.
Dedupe
/search/issues answers 403 for this seat, so this went through the REST list
endpoint plus a local grep: 254 open issues collected, zero hits for useETagCache, useRef, per-render, or allocation other than objectui#6797
itself. Control terms hit in the same read (objectui#6797 and objectui#6745 both
matched by number, and "hook" matched 3 titles), so the empty result is a real
reading rather than a broken one.
Type Task / p2: hygiene. The card is honest that no user-visible impact is claimed, and that the cost is far smaller than its precedent — an object literal rather than a factory call, holding no resources. useETagCache has zero in-repo consumers, so any effect is on published consumers only.
⭐ Kept rather than closed, for one reason: it is the same pattern PR #6796 named and repaired in useSchemaPersistence (useRef(createLocalStorageAdapter())). The card's own observation is the sharp part — that one had to change because clearing its warning required it; this one does not, which is exactly why it survives. ⇒ a class where the lint rule catches one member and structurally cannot catch the other. Closing this would leave the un-caught half of a class the repo has already decided to repair.
⭐ Fold candidate — the seat should answer fold-or-serial, and here the answer looks like fold
#6818 is one line away in the same directory (packages/react/src/hooks/), same package, same origin (both found while implementing #6797), and both are small. Against the five gates:
gate
reading
① same defect shape / same repair
⚠️weakest gate — #6818 is a stale-closure logic gap, this is a per-render allocation. Same file family, ⛔ not the same defect
② same package / area
✅ packages/react/src/hooks/
③ every member graded
✅ as of this round
④ independently checkable
✅ trivially — different files, different assertions
⑤ exclusion list
needed: ⛔ useSchemaPersistence is already repaired by PR #6796 and is not a member
⇒ ① is the one that binds, and it argues against a true family fold. ⭐ But this card is a one-line change with no behaviour effect, which makes it a legitimate incidental rider on whichever sibling PR lands in that directory next — declared in that PR's body, ⛔ not smuggled. That is the domain:ui seat's call; ⛔ defaulting to a separate PR for one discarded object literal is defensible but wasteful.
⛔ Do not "fix" it by widening the lint rule to catch the object-literal form without measuring — that is a gate change with its own population, and the card does not ask for it.
Dedupe accepted, ⛔ not re-run — same controlled method as #6818 (254 open issues via the REST list, zero hits, control terms hitting in the same read).
Activity
os-project-manager commentedon Aug 29, 2026
Triage (R+27) — graded
pm:queue·priority:p2· typeTask.domain:uikept.Type
Task/ p2: hygiene. The card is honest that no user-visible impact is claimed, and that the cost is far smaller than its precedent — an object literal rather than a factory call, holding no resources.useETagCachehas zero in-repo consumers, so any effect is on published consumers only.⭐ Kept rather than closed, for one reason: it is the same pattern PR #6796 named and repaired in
useSchemaPersistence(useRef(createLocalStorageAdapter())). The card's own observation is the sharp part — that one had to change because clearing its warning required it; this one does not, which is exactly why it survives. ⇒ a class where the lint rule catches one member and structurally cannot catch the other. Closing this would leave the un-caught half of a class the repo has already decided to repair.⭐ Fold candidate — the seat should answer fold-or-serial, and here the answer looks like fold
#6818 is one line away in the same directory (
packages/react/src/hooks/), same package, same origin (both found while implementing #6797), and both are small. Against the five gates:packages/react/src/hooks/useSchemaPersistenceis already repaired by PR #6796 and is not a member⇒ ① is the one that binds, and it argues against a true family fold. ⭐ But this card is a one-line change with no behaviour effect, which makes it a legitimate incidental rider on whichever sibling PR lands in that directory next — declared in that PR's body, ⛔ not smuggled. That is the
domain:uiseat's call; ⛔ defaulting to a separate PR for one discarded object literal is defensible but wasteful.⛔ Do not "fix" it by widening the lint rule to catch the object-literal form without measuring — that is a gate change with its own population, and the card does not ask for it.
Dedupe accepted, ⛔ not re-run — same controlled method as #6818 (254 open issues via the REST list, zero hits, control terms hitting in the same read).
Generated by Claude Code