From 4de408aeb3a2db21feaaa79bbf966f586b92cc9c Mon Sep 17 00:00:00 2001 From: webstrand Date: Wed, 2 Apr 2025 15:46:19 -0400 Subject: [PATCH 1/2] Avoid notifying non-existent members on Set.clear() --- .changeset/funny-baths-drive.md | 5 +++++ packages/set/src/index.ts | 13 +++++++------ packages/set/test/index.test.ts | 23 +++++++++++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 .changeset/funny-baths-drive.md diff --git a/.changeset/funny-baths-drive.md b/.changeset/funny-baths-drive.md new file mode 100644 index 000000000..2b33ca475 --- /dev/null +++ b/.changeset/funny-baths-drive.md @@ -0,0 +1,5 @@ +--- +"@solid-primitives/set": minor +--- + +Avoid notifying non-existent members on Set.clear() diff --git a/packages/set/src/index.ts b/packages/set/src/index.ts index 26d81653c..b26c64960 100644 --- a/packages/set/src/index.ts +++ b/packages/set/src/index.ts @@ -88,13 +88,14 @@ export class ReactiveSet extends Set { } clear(): void { - if (super.size) { + if (!super.size) return; + batch(() => { + this.#triggers.dirty($KEYS); + for (const member of super.values()) { + this.#triggers.dirty(member); + } super.clear(); - - batch(() => { - this.#triggers.dirtyAll(); - }); - } + }); } } diff --git a/packages/set/test/index.test.ts b/packages/set/test/index.test.ts index 412fba986..e7e46c67d 100644 --- a/packages/set/test/index.test.ts +++ b/packages/set/test/index.test.ts @@ -182,6 +182,29 @@ describe("ReactiveSet", () => { dispose(); }); + + test("clear notifies only listeners of existing members", () => + createRoot(dispose => { + const set = new ReactiveSet([1, 2, 3, 4]); + + const existing = vi.fn(); + createComputed(() => existing(set.has(2))); + + const nonexisting = vi.fn(); + createComputed(() => nonexisting(set.has(5))); + + expect(existing).toHaveBeenNthCalledWith(1, true); + expect(nonexisting).toHaveBeenNthCalledWith(1, false); + + set.clear(); + + expect(existing).toHaveBeenCalledTimes(2); + expect(existing).toHaveBeenNthCalledWith(2, false); + + expect(nonexisting).toHaveBeenCalledTimes(1); + + dispose(); + })); }); describe("ReactiveWeakSet", () => { From 1d1c68659a7642be0aaf47fbcf3c639713bbfbc8 Mon Sep 17 00:00:00 2001 From: webstrand Date: Wed, 2 Apr 2025 15:57:28 -0400 Subject: [PATCH 2/2] Avoid notifying non-existent members on Map.clear() --- .changeset/red-islands-tickle.md | 5 ++++ packages/map/src/index.ts | 17 ++++++++------ packages/map/test/index.test.ts | 39 ++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 .changeset/red-islands-tickle.md diff --git a/.changeset/red-islands-tickle.md b/.changeset/red-islands-tickle.md new file mode 100644 index 000000000..2d1d8f28a --- /dev/null +++ b/.changeset/red-islands-tickle.md @@ -0,0 +1,5 @@ +--- +"@solid-primitives/map": minor +--- + +Avoid notifying non-existent members on Map.clear() diff --git a/packages/map/src/index.ts b/packages/map/src/index.ts index 489c5bc76..eeba7a344 100644 --- a/packages/map/src/index.ts +++ b/packages/map/src/index.ts @@ -120,14 +120,17 @@ export class ReactiveMap extends Map { } clear(): void { - if (super.size) { - super.clear(); + if (super.size === 0) return; + batch(() => { + this.#keyTriggers.dirty($OBJECT); + this.#valueTriggers.dirty($OBJECT); + for (const key of super.keys()) { + this.#keyTriggers.dirty(key); + this.#valueTriggers.dirty(key); + } - batch(() => { - this.#keyTriggers.dirtyAll(); - this.#valueTriggers.dirtyAll(); - }); - } + super.clear(); + }); } } diff --git a/packages/map/test/index.test.ts b/packages/map/test/index.test.ts index ccc5f2898..6c9b84cae 100644 --- a/packages/map/test/index.test.ts +++ b/packages/map/test/index.test.ts @@ -378,6 +378,45 @@ describe("ReactiveMap", () => { dispose(); }); + test(".clear() notifies only listeners of existing members", () => + createRoot(dispose => { + const map = new ReactiveMap([ + [1, "a"], + [2, "b"], + [3, "c"], + ]); + + const existingKey = vi.fn(); + createComputed(() => existingKey(map.has(2))); + + const existingValue = vi.fn(); + createComputed(() => existingValue(map.get(2))); + + const nonexistingKey = vi.fn(); + createComputed(() => nonexistingKey(map.has(4))); + + const nonexistingValue = vi.fn(); + createComputed(() => nonexistingValue(map.get(4))); + + expect(existingKey).toHaveBeenNthCalledWith(1, true); + expect(existingValue).toHaveBeenNthCalledWith(1, "b"); + + expect(nonexistingKey).toHaveBeenNthCalledWith(1, false); + expect(nonexistingValue).toHaveBeenNthCalledWith(1, undefined); + + map.clear(); + + expect(existingKey).toHaveBeenCalledTimes(2); + expect(existingKey).toHaveBeenNthCalledWith(2, false); + + expect(existingValue).toHaveBeenCalledTimes(2); + expect(existingValue).toHaveBeenNthCalledWith(2, undefined); + + expect(nonexistingKey).toHaveBeenCalledTimes(1); + expect(nonexistingValue).toHaveBeenCalledTimes(1); + + dispose(); + })); }); describe("ReactiveWeakMap", () => {