Skip to content

[FIX] owl-core: signal collections track per-key, not per-signal#1903

Open
ged-odoo wants to merge 3 commits into
masterfrom
master-collections-fix-ged
Open

[FIX] owl-core: signal collections track per-key, not per-signal#1903
ged-odoo wants to merge 3 commits into
masterfrom
master-collections-fix-ged

Conversation

@ged-odoo
Copy link
Copy Markdown
Contributor

Reading .has(k) / .get(k) on a signal.Set / signal.Map / signal.WeakMap was subscribing to the signal atom instead of a per-key atom, so mutating any key (e.g. signal.Set(...).add(2)) re-ran every observer of the collection — even one that only read has(1).

The collection helpers in proxy.ts now pass null to onReadTargetKey / onWriteTargetKey, restoring per-key tracking. Shallow inner-value wrapping still respects the signal atom via possiblyReactive, so the existing "X item is not reactive" tests keep passing.

Closes #1902

Reading `.has(k)` / `.get(k)` on a `signal.Set` / `signal.Map` /
`signal.WeakMap` was subscribing to the signal atom instead of a per-key
atom, so mutating *any* key (e.g. `signal.Set(...).add(2)`) re-ran every
observer of the collection — even one that only read `has(1)`.

The collection helpers in proxy.ts now pass `null` to onReadTargetKey /
onWriteTargetKey, restoring per-key tracking. Shallow inner-value
wrapping still respects the signal atom via possiblyReactive, so the
existing "X item is not reactive" tests keep passing.

Closes #1902
@ged-odoo ged-odoo force-pushed the master-collections-fix-ged branch from 2e2928b to 6861c35 Compare May 28, 2026 11:30
ged-odoo added 2 commits May 28, 2026 13:36
Spell out what signal.Array / signal.Object / signal.Set / signal.Map
actually do: the proxy wrapping, the .set(newValue) replacement API, the
shallow caveat, and — newly — the tracking granularity asymmetry.

signal.Array and signal.Object invalidate the whole signal on any write;
signal.Set and signal.Map track per-key, matching the behavior of
proxy(). The latter was a documented-elsewhere assumption that confused
users (cf. #1902); it now has a concrete example.
…ment

signal.Array / signal.Object / signal.Set / signal.Map now default to an
empty collection when no initial value is passed, so the common case of
"reactive bag, fill it later" reads more naturally:

  const items = signal.Array<Todo>();
  const cache = signal.Map<string, User>();

Suggested by seb-odoo in #1902 (Note 2).
@ged-odoo ged-odoo force-pushed the master-collections-fix-ged branch from 6861c35 to 8a5076c Compare May 28, 2026 11:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

signal.Set "has" observes too much

1 participant