From d2063cae7e8f6559db9ec8a5f666947dd893acf3 Mon Sep 17 00:00:00 2001 From: Azarattum Date: Mon, 22 Apr 2024 16:21:31 +0700 Subject: [PATCH 1/3] breaking: finer lazy reactivity for set --- packages/svelte/src/reactivity/set.js | 68 ++++++++++++---------- packages/svelte/src/reactivity/set.test.ts | 6 +- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/packages/svelte/src/reactivity/set.js b/packages/svelte/src/reactivity/set.js index 1855090cdfa..f94ea220df2 100644 --- a/packages/svelte/src/reactivity/set.js +++ b/packages/svelte/src/reactivity/set.js @@ -1,5 +1,6 @@ import { DEV } from 'esm-env'; import { source, set } from '../internal/client/reactivity/sources.js'; +import { effect } from '../internal/client/reactivity/effects.js'; import { get } from '../internal/client/runtime.js'; import { map } from './utils.js'; @@ -13,7 +14,7 @@ var inited = false; */ export class ReactiveSet extends Set { /** @type {Map>} */ - #sources = new Map(); + #tracked = new Map(); #version = source(0); #size = source(0); @@ -27,13 +28,11 @@ export class ReactiveSet extends Set { if (DEV) new Set(value); if (value) { - var sources = this.#sources; - for (var element of value) { - sources.set(element, source(true)); + super.add(element); } - this.#size.v = sources.size; + this.#size.v = super.size; } if (!inited) this.#init(); @@ -73,27 +72,36 @@ export class ReactiveSet extends Set { /** @param {T} value */ has(value) { - var s = this.#sources.get(value); + var exists = super.has(value); + var s = this.#tracked.get(value); if (s === undefined) { - // We should always track the version in case - // the Set ever gets this value in the future. - get(this.#version); - - return false; + s = source(exists); + this.#tracked.set(value, s); } + effect(() => () => { + queueMicrotask(() => { + if (s && !s.reactions) { + this.#tracked.delete(value); + } + }); + }); + return get(s); } /** @param {T} value */ add(value) { - var sources = this.#sources; - - if (!sources.has(value)) { - sources.set(value, source(true)); - set(this.#size, sources.size); + if (!super.has(value)) { + super.add(value); + set(this.#size, super.size); this.#increment_version(); + + var s = this.#tracked.get(value); + if (s !== undefined) { + set(s, true); + } } return this; @@ -101,37 +109,35 @@ export class ReactiveSet extends Set { /** @param {T} value */ delete(value) { - var sources = this.#sources; - var s = sources.get(value); - - if (s !== undefined) { - var removed = sources.delete(value); - set(this.#size, sources.size); - set(s, false); + var removed = super.delete(value); + if (removed) { + set(this.#size, super.size); this.#increment_version(); - return removed; + + var s = this.#tracked.get(value); + if (s !== undefined) { + set(s, false); + } } - return false; + return removed; } clear() { - var sources = this.#sources; - - if (sources.size !== 0) { + if (super.size !== 0) { set(this.#size, 0); - for (var s of sources.values()) { + for (var s of this.#tracked.values()) { set(s, false); } this.#increment_version(); } - sources.clear(); + super.clear(); } keys() { get(this.#version); - return map(this.#sources.keys(), (key) => key, 'Set Iterator'); + return map(super.keys(), (key) => key, 'Set Iterator'); } values() { diff --git a/packages/svelte/src/reactivity/set.test.ts b/packages/svelte/src/reactivity/set.test.ts index a855ede6b38..e858efd1723 100644 --- a/packages/svelte/src/reactivity/set.test.ts +++ b/packages/svelte/src/reactivity/set.test.ts @@ -30,7 +30,7 @@ test('set.values()', () => { set.clear(); }); - assert.deepEqual(log, [5, true, [1, 2, 3, 4, 5], 4, false, [1, 2, 4, 5], 0, false, []]); + assert.deepEqual(log, [5, true, [1, 2, 3, 4, 5], 4, false, [1, 2, 4, 5], 0, []]); cleanup(); }); @@ -58,6 +58,10 @@ test('set.has(...)', () => { set.delete(2); }); + flushSync(() => { + set.add(4); + }); + flushSync(() => { set.add(2); }); From 69bae17de3d4c7b9aa363d3755758faec3842b84 Mon Sep 17 00:00:00 2001 From: Azarattum Date: Tue, 23 Apr 2024 08:18:53 +0700 Subject: [PATCH 2/3] fix: defer call to has till needed --- packages/svelte/src/reactivity/set.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/svelte/src/reactivity/set.js b/packages/svelte/src/reactivity/set.js index f94ea220df2..64e77e013b7 100644 --- a/packages/svelte/src/reactivity/set.js +++ b/packages/svelte/src/reactivity/set.js @@ -72,11 +72,10 @@ export class ReactiveSet extends Set { /** @param {T} value */ has(value) { - var exists = super.has(value); var s = this.#tracked.get(value); if (s === undefined) { - s = source(exists); + s = source(super.has(value)); this.#tracked.set(value, s); } From 3775935163baf4a27355b0a48bd48cbaa6a25881 Mon Sep 17 00:00:00 2001 From: Azarattum Date: Tue, 23 Apr 2024 08:20:10 +0700 Subject: [PATCH 3/3] chore: regenerate and improve set typings --- packages/svelte/src/reactivity/set.js | 1 + packages/svelte/types/index.d.ts | 10 +--------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/svelte/src/reactivity/set.js b/packages/svelte/src/reactivity/set.js index 64e77e013b7..c37fccd1bb4 100644 --- a/packages/svelte/src/reactivity/set.js +++ b/packages/svelte/src/reactivity/set.js @@ -11,6 +11,7 @@ var inited = false; /** * @template T + * @extends {Set} */ export class ReactiveSet extends Set { /** @type {Map>} */ diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 900bee5a769..4c31ff8e8a2 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1984,19 +1984,11 @@ declare module 'svelte/reactivity' { constructor(...values: any[]); #private; } - class ReactiveSet extends Set { + class ReactiveSet extends Set { constructor(value?: Iterable | null | undefined); - has(value: T): boolean; - add(value: T): this; - - delete(value: T): boolean; - keys(): IterableIterator; - values(): IterableIterator; - entries(): IterableIterator<[T, T]>; - [Symbol.iterator](): IterableIterator; #private; } class ReactiveMap extends Map {