test(svelte-store): pin useSelector memoization - #1
Merged
seongwon030 merged 1 commit intoJul 27, 2026
Conversation
Adds a controlled measurement of whether useSelector memoizes an unchanged slice, alongside the regression test already on this branch. Three arms share one button, so a failure says why it failed: an object slice on the library's own compare, the same slice behind a compare no proxy can fool, and a primitive slice Svelte never proxies. Only the first regresses when the slice is proxied, which pins the cause to the proxying rather than to the store or the subscription. Reverting $state.raw to $state reads 4 renders over 4 distinct identities, against 1 for either control. Identities are counted rather than only renders: re-setting the slice mints a brand-new Proxy per notification, so the count separates "notified again" from "notified with something genuinely new". The updates are clicked one at a time. Svelte coalesces a synchronous burst into a single effect run, which would hide a cost paid once per notification. One test asserts no state_proxy_equality_mismatch is emitted. Svelte warns there only when both operands are the same underlying object yet === disagrees, so it fires precisely when the slice is wrapped.
seongwon030
merged commit Jul 27, 2026
dd5da7b
into
seongwon030:fix/svelte-store-use-selector-proxy-equality
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The harness offered in TanStack#334, ported into
packages/svelte-store/testsas you asked. One commit on top offix/svelte-store-use-selector-proxy-equality, tests only — no source or changeset changes, since your commit already covers both.What it adds
A controlled measurement to sit beside the regression test already on the branch.
ProxyEquality.test.svelteestablishes that an unrelated update stops re-rendering; this establishes how much was being paid and why, so a future regression reports a diagnosis rather than a bare count.Three arms share one button, so a single click measures all of them under identical conditions:
===a.id === b.id— a compare no proxy can fool (control)Only the default arm regresses when the slice is proxied. That is what pins the cause to the proxying rather than to the store or the subscription — the controls are the point, not padding.
Red/green
Reverting
$state.rawback to$stateon this branch:$state.raw$state17 pass on the branch; 3 fail when reverted. Because every counter renders into the DOM, the failure output prints the whole experiment at once:
Three details worth flagging
Identities are counted, not just renders. Re-setting the slice mints a brand-new
Proxyper notification —proxy()has no per-target cache — sosource.equals(new_proxy)is false and every dependent re-runs. Counting distinct identities separates "notified again" from "notified with something genuinely new".The updates are clicked one at a time. Svelte coalesces a synchronous burst into a single effect run, which would hide a cost paid once per notification.
One test asserts no
state_proxy_equality_mismatchis emitted. Svelte's instrumented===warns only when(a === b) !== (get_proxied_value(a) === get_proxied_value(b))— that is, only when both operands are one underlying object yet===disagrees. It therefore fires precisely when the slice is wrapped, which makes it a tight guard against a revert to$state(...).Conventions
Written to match the existing suite: jsdom +
@testing-library/svelte, counters rendered into the DOM, one props-free component per scenario. No type annotations in the.sveltefile —eslint-plugin-svelteis not configured with the TS parser here, so any annotation is a parse error, which is presumably why no existing component carries one.One
eslint-disableforsvelte/prefer-svelte-reactivity: the identity set is deliberately a plainSet, because aSvelteSetwould pull the instrument into the reactivity it is measuring. The reason is in a comment above it.Verified clean:
vitest17 passed,eslint0 problems,svelte-check0 errors / 0 warnings,prettier --checkclean, no Svelte compiler warnings.Happy to adjust naming, drop the warning assertion, or split the arms into separate components if you would rather they match the one-file-per-scenario shape more literally.