Skip to content

Version 1.6.0

Choose a tag to compare

@estherbrunner estherbrunner released this 05 Oct 11:40
· 5 commits to main since this release
Immutable release. Only release title and notes can be modified.
b74d60c

What's Changed

Added

  • map() / forEach() on MutableList and DerivedList (src/nodes/list.ts, src/nodes/collection.ts): Both visit every item in list order, passing each item's signal and its stable string key — map<R>(callbackfn: (item: S, key: string) => R): R[] and forEach(callbackfn: (item: S, key: string) => void): void, with the parameter order following Map.prototype.forEach((value, key) => …). Under createList(…, { createItem: createStore }), S is the item's MutableStore<T>. map returns a plain array snapshot, not a signal — reactive mapping over values remains deriveList's job. Tracking is identical to [Symbol.iterator] (ADR-0015 class): one structural subscription, no internal signal.get(), so reads inside the callback track as usual. A map/forEach-only effect re-runs on add, remove, reorder, and replace(key, v); byKey(k).set(v) re-runs it only after list.get() has linked the item signal to the list node. The key array is snapshotted before traversal, so a callback that mutates the list does not change the current pass. Both methods are declared on the MutableList/DerivedList types, so the deprecated List/Collection aliases inherit them. Motivated by keyed JSX list rendering, where a compiler lowers .map((item, key) => …) to a keyed reconcile over item signals. See ADR-0019.

Fixed

  • Per-item signals of a per-item deriveList(source, fn) gated propagation on ===, so a fresh deep-equal derived item over-notified structural subscribers (src/nodes/collection.ts): Previously, the per-item Memo (sync callback) and per-item Task (async callback) were built without an equals option and fell back to strict equality — the only per-slice derivation path that did, against ADR-0018's rule that per-slice equals stops propagation for an unchanged property or item (deriveStore's per-property memos, the keyed adapter's item signals, and mutable list item states all default to DEEP_EQUALITY). When the callback returned a fresh but deep-equal object — e.g. deriveList(users, u => ({ name: u.name })) while an unrelated field of the source item changed — the === gate failed, the derived node rebuilt its array, and every structural subscriber (iterator, map, forEach, get()) re-ran although the derived content was unchanged. Now both per-item signals use equals: DEEP_EQUALITY, so a deep-equal recompute stops at the per-item signal and only a genuine derived-content change reaches the node. Effects that read derived values observe no content difference; only object-identity checks on derived items could distinguish the old behavior, and keys — not object references — are identity in this library. The derived node's own element-identity array comparison is unchanged: it only ever sees arrays whose element references genuinely changed.

Full Changelog: v1.5.2...v1.6.0