Summary
useIconChecker in src/components/itemIconUtils.ts is documented as returning a "stable predicate" but allocates a fresh closure on every render. Stable-by-name, not stable-by-reference. Doesn't break correctness today, but any consumer that relies on === referential stability (memoized children, dependency arrays) sees a new function each render.
Fix sketch
Wrap the returned closure in useMemo keyed on state.manifest:
const checker = useMemo(
() => (category: CategoryId, id: string) => {
if (state.kind !== 'present') return false;
return Boolean(resolveIconUrl(state.manifest, category, id));
},
[state.manifest, state.kind]
);
Or relax the docstring to "logically stable" if we don't actually need referential stability. Cheap to fix.
Acceptance criteria
References
Summary
useIconCheckerinsrc/components/itemIconUtils.tsis documented as returning a "stable predicate" but allocates a fresh closure on every render. Stable-by-name, not stable-by-reference. Doesn't break correctness today, but any consumer that relies on===referential stability (memoized children, dependency arrays) sees a new function each render.Fix sketch
Wrap the returned closure in
useMemokeyed onstate.manifest:Or relax the docstring to "logically stable" if we don't actually need referential stability. Cheap to fix.
Acceptance criteria
useIconChecker's returned function is referentially stable across renders when the manifest hasn't changed.checkerfrom two consecutive renders is===when the manifest is unchanged.References