diff --git a/src/runtime/store/index.ts b/src/runtime/store/index.ts index e7db2284019f..5ab6cfa26b58 100644 --- a/src/runtime/store/index.ts +++ b/src/runtime/store/index.ts @@ -10,7 +10,7 @@ type Unsubscriber = () => void; type Updater = (value: T) => T; /** Cleanup logic callback. */ -type Invalidater = (value?: T) => void; +type Invalidator = (value?: T) => void; /** Start and stop notification callbacks. */ type StartStopNotifier = (set: Subscriber) => Unsubscriber | void; @@ -22,7 +22,7 @@ export interface Readable { * @param run subscription callback * @param invalidate cleanup callback */ - subscribe(run: Subscriber, invalidate?: Invalidater): Unsubscriber; + subscribe(run: Subscriber, invalidate?: Invalidator): Unsubscriber; } /** Writable interface for both updating and subscribing. */ @@ -41,7 +41,7 @@ export interface Writable extends Readable { } /** Pair of subscriber and invalidator. */ -type SubscribeInvalidateTuple = [Subscriber, Invalidater]; +type SubscribeInvalidateTuple = [Subscriber, Invalidator]; /** * Creates a `Readable` store that allows reading by subscription. @@ -78,7 +78,7 @@ export function writable(value: T, start: StartStopNotifier = noop): Writa set(fn(value)); } - function subscribe(run: Subscriber, invalidate: Invalidater = noop): Unsubscriber { + function subscribe(run: Subscriber, invalidate: Invalidator = noop): Unsubscriber { const subscriber: SubscribeInvalidateTuple = [run, invalidate]; subscribers.push(subscriber); if (subscribers.length === 1) { @@ -127,7 +127,9 @@ export function derived( const auto = fn.length < 2; - return readable(initial_value, (set) => { + const invalidators: Array> = []; + + const store = readable(initial_value, (set) => { let inited = false; const values: StoresValues = [] as StoresValues; @@ -156,6 +158,7 @@ export function derived( } }, () => { + run_all(invalidators); pending |= (1 << i); }), ); @@ -168,6 +171,22 @@ export function derived( cleanup(); }; }); + + return { + subscribe(run: Subscriber, invalidate: Invalidator = noop): Unsubscriber { + invalidators.push(invalidate); + + const unsubscribe = store.subscribe(run, invalidate); + + return () => { + const index = invalidators.indexOf(invalidate); + if (index !== -1) { + invalidators.splice(index, 1); + } + unsubscribe(); + }; + } + } } /** @@ -178,4 +197,4 @@ export function get(store: Readable): T { let value: T | undefined; store.subscribe((_: T) => value = _)(); return value as T; -} +} \ No newline at end of file diff --git a/test/store/index.ts b/test/store/index.ts index 13b8e1f8693c..77ef9f95493b 100644 --- a/test/store/index.ts +++ b/test/store/index.ts @@ -189,6 +189,34 @@ describe('store', () => { unsubscribe(); }); + it('prevents diamond dependency problem', () => { + const count = writable(0); + const values = []; + + const a = derived(count, $count => { + return 'a' + $count; + }); + + const b = derived(count, $count => { + return 'b' + $count; + }); + + const combined = derived([a, b], ([a, b]) => { + return a + b; + }); + + const unsubscribe = combined.subscribe(v => { + values.push(v); + }); + + assert.deepEqual(values, ['a0b0']); + + count.set(1); + assert.deepEqual(values, ['a0b0', 'a1b1']); + + unsubscribe(); + }); + it('is updated with safe_not_equal logic', () => { const arr = [0];