From 8c1d4d1f41da8c71817682ce5e4434e67032a269 Mon Sep 17 00:00:00 2001 From: Michael Beckemeyer Date: Fri, 4 Oct 2024 12:17:11 +0200 Subject: [PATCH] Add missing forEach methods --- packages/reactivity-core/CHANGELOG.md | 1 + .../reactivity-core/collections/map.test.ts | 30 ++++++++++++++++++- packages/reactivity-core/collections/map.ts | 15 ++++++++++ .../reactivity-core/collections/set.test.ts | 27 ++++++++++++++++- packages/reactivity-core/collections/set.ts | 11 +++++++ 5 files changed, 82 insertions(+), 2 deletions(-) diff --git a/packages/reactivity-core/CHANGELOG.md b/packages/reactivity-core/CHANGELOG.md index c9e7222..6a736d5 100644 --- a/packages/reactivity-core/CHANGELOG.md +++ b/packages/reactivity-core/CHANGELOG.md @@ -3,6 +3,7 @@ ## v0.4.3 (Unreleased) - Introduce `subtleWatchDirty`, a function that allows one to watch for signal changes without triggering the re-evaluation of the signal. +- Add missing `forEach()` method to `ReactiveSet` and `ReactiveMap`. - Deprecate `syncEffectOnce` (use `subtleWatchDirty` instead). ## v0.4.2 diff --git a/packages/reactivity-core/collections/map.test.ts b/packages/reactivity-core/collections/map.test.ts index 095f891..719d3c8 100644 --- a/packages/reactivity-core/collections/map.test.ts +++ b/packages/reactivity-core/collections/map.test.ts @@ -1,4 +1,4 @@ -import { it, expect, describe } from "vitest"; +import { it, expect, describe, vi } from "vitest"; import { ReactiveMap, reactiveMap } from "./map"; import { batch, computed } from "../ReactiveImpl"; import { syncEffect } from "../sync"; @@ -45,6 +45,34 @@ describe("basic API", () => { expect(removed).toBe(false); }); + it("supports iteration via forEach", () => { + const map = reactiveMap([ + ["foo", 1], + ["bar", 2] + ]); + map.set("baz", 3); + + const cb = vi.fn(); + map.forEach(cb); + + expect(cb.mock.calls).toMatchInlineSnapshot(` + [ + [ + 1, + "foo", + ], + [ + 2, + "bar", + ], + [ + 3, + "baz", + ], + ] + `); + }); + it("supports iteration", () => { const map = reactiveMap([ ["foo", 1], diff --git a/packages/reactivity-core/collections/map.ts b/packages/reactivity-core/collections/map.ts index 68afb89..acb3f81 100644 --- a/packages/reactivity-core/collections/map.ts +++ b/packages/reactivity-core/collections/map.ts @@ -42,6 +42,13 @@ export interface ReactiveMap extends Iterable<[key: K, value: V]> { */ delete(key: K): boolean; + /** + * Executes the given callback for every entry of the map. + * + * See also [Map.forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach). + */ + forEach(callback: (value: V, key: K) => void): void; + /** * Returns an iterator over the `[key, value]` entries in this map. */ @@ -108,6 +115,14 @@ class ReactiveMapImpl implements ReactiveMap { return this.#map.size; } + forEach(callback: (value: V, key: K) => void): void { + this.#subscribeToStructureChange(); + const entries = this.#map.entries(); + for (const [key, cell] of entries) { + callback(cell.value, key); + } + } + entries(): IterableIterator<[K, V]> { this.#subscribeToStructureChange(); return this.#iterateEntries(); diff --git a/packages/reactivity-core/collections/set.test.ts b/packages/reactivity-core/collections/set.test.ts index 7b212fb..8390894 100644 --- a/packages/reactivity-core/collections/set.test.ts +++ b/packages/reactivity-core/collections/set.test.ts @@ -1,4 +1,4 @@ -import { it, expect, describe } from "vitest"; +import { it, expect, describe, vi } from "vitest"; import { reactiveSet } from "./set"; describe("basic API", () => { @@ -31,6 +31,31 @@ describe("basic API", () => { expect(removed).toBe(false); }); + it("supports iteration via forEach", () => { + const set = reactiveSet(["foo", "bar"]); + set.add("baz"); + + const cb = vi.fn(); + set.forEach(cb); + + expect(cb.mock.calls).toMatchInlineSnapshot(` + [ + [ + "foo", + "foo", + ], + [ + "bar", + "bar", + ], + [ + "baz", + "baz", + ], + ] + `); + }); + it("supports iteration", () => { const set = reactiveSet(["foo", "bar"]); set.add("baz"); diff --git a/packages/reactivity-core/collections/set.ts b/packages/reactivity-core/collections/set.ts index 4cdbc54..1aec460 100644 --- a/packages/reactivity-core/collections/set.ts +++ b/packages/reactivity-core/collections/set.ts @@ -36,6 +36,13 @@ export interface ReactiveSet extends Iterable { */ delete(value: V): boolean; + /** + * Executes the given callback for every entry of the set. + * + * See also [Set.forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/forEach). + */ + forEach(callback: (value: V, key: V) => void): void; + /** * Returns an iterator over the `[value, value]` entries in this set. * @@ -110,6 +117,10 @@ export class ReactiveSetImpl implements ReactiveSet { return this.#impl.delete(value); } + forEach(callback: (value: V, key: V) => void): void { + this.#impl.forEach((_value, key) => callback(key, key)); + } + entries(): IterableIterator<[value: V, value: V]> { return mapToDuplicateEntries(this.values()); }