fix(createRegistry): skip values/keys/entries cache when reactive#209
Merged
Conversation
|
commit: |
When `reactive: true`, `values()`/`keys()`/`entries()` now iterate the underlying collection on every call instead of serving a cached array. The cache was silently dropping Vue's iteration dependency on warm re-runs, so a computed that iterated a reactive registry would lose its Map-iteration dep after any re-run that hit the cache — subsequent mutations then failed to propagate. Refs #208
6b6e156 to
aa339f7
Compare
…-run Three regression tests for values()/keys()/entries() that would have caught #208. Each wires a computed that first runs through a non-iteration dep (ref bump), then mutates the registry and asserts the computed picks up the change. Verified to fail without the cache skip in the reactive path.
…e fix The tip on create-registry.md steered users away from `reactive: true` toward `useProxyRegistry` because `values()` caching broke iteration deps. That bug is fixed (see PR #209) — rewrite the tip to recommend `reactive: true` for template/computed iteration and position `useProxyRegistry` as the tool for event-driven snapshots, deep tracking, or cases where wrapping tickets isn't desired. Qualify the \"What's NOT Reactive\" table in the reactivity guide with a pointer to the reactive-option pattern. Simplify Pattern 1 so the example calls the public `registry.values()` / `registry.size` rather than poking at `registry.collection` directly, now that those are reactive too. Refs #208 #209
This was referenced Apr 23, 2026
johnleider
added a commit
that referenced
this pull request
Apr 23, 2026
- `.claude/rules/composables.md`: new section "Plugins and Reactive Defaults" covering the primitive/plugin tiers, when to pass `reactive: true` to an internal registry, the two-registry architecture, and how `reactive: true` and `useProxyRegistry` complement each other. - `PHILOSOPHY.md §4.4`: rewrite from "the reactive: true footgun" to "Registry reactivity". The footgun described (values() cache dropping Vue dep tracking) was closed in #209 — that section was actively teaching the wrong mental model through #210, #211, and #212. New text positions `reactive: true` and `useProxyRegistry` as two valid complementary options. - `PHILOSOPHY.md §6.7`: revise the closing warning that said "do not substitute reactive: true on the registry itself" — same reason. Refs #208 #209 #210 #211 #212
johnleider
added a commit
that referenced
this pull request
Apr 23, 2026
#213) - `.claude/rules/composables.md`: new section "Plugins and Reactive Defaults" covering the primitive/plugin tiers, when to pass `reactive: true` to an internal registry, the two-registry architecture, and how `reactive: true` and `useProxyRegistry` complement each other. - `PHILOSOPHY.md §4.4`: rewrite from "the reactive: true footgun" to "Registry reactivity". The footgun described (values() cache dropping Vue dep tracking) was closed in #209 — that section was actively teaching the wrong mental model through #210, #211, and #212. New text positions `reactive: true` and `useProxyRegistry` as two valid complementary options. - `PHILOSOPHY.md §6.7`: revise the closing warning that said "do not substitute reactive: true on the registry itself" — same reason. Refs #208 #209 #210 #211 #212
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.
Summary
Skip the
keys()/values()/entries()cache increateRegistrywhen the registry is created withreactive: true. The cache was silently dropping Vue's iteration dependency on warm re-runs, so a computed iterating a reactive registry would lose its Map-iteration dep after any re-run that hit the cache — subsequent mutations then failed to propagate.Background
Alternative fix for the bug in #208. That PR works around the symptom by mutating the existing ticket in `upsert` via `Object.assign` so its `shallowReactive` proxy fires — which happens to land on a different live dep path (per-ticket `.value`). The underlying iteration-dep hole stays open for any other consumer.
This PR fixes the hole at the source:
Why the cache broke dep tracking
Vue's computed re-tracks deps on every run. When a computed iterated `values()`:
`select()` doesn't `invalidate()`, so any re-run triggered by a ref change after the initial render dropped the iteration dep. Subsequent `upsert`s then had nothing listening.
Reactive consumers tend to have small collections (UI state), so skipping the cache in that mode has negligible cost. Non-reactive registries keep the cache unchanged.
Follow-ups (separate PRs)