diff --git a/src/runtime/store/index.ts b/src/runtime/store/index.ts index e947fa074028..afd4ccabd61e 100644 --- a/src/runtime/store/index.ts +++ b/src/runtime/store/index.ts @@ -13,7 +13,7 @@ export type Updater = (value: T) => T; type Invalidator = (value?: T) => void; /** Start and stop notification callbacks. */ -export type StartStopNotifier = (set: Subscriber) => Unsubscriber | void; +export type StartStopNotifier = (set: Subscriber, update?: (fn: Updater) => void) => Unsubscriber | void; /** Readable interface for subscribing. */ export interface Readable { @@ -92,7 +92,7 @@ export function writable(value?: T, start: StartStopNotifier = noop): Writ const subscriber: SubscribeInvalidateTuple = [run, invalidate]; subscribers.add(subscriber); if (subscribers.size === 1) { - stop = start(set) || noop; + stop = start(set, update) || noop; } run(value); @@ -125,7 +125,7 @@ type StoresValues = T extends Readable ? U : */ export function derived( stores: S, - fn: (values: StoresValues, set: (value: T) => void) => Unsubscriber | void, + fn: (values: StoresValues, set: Subscriber, update?: (fn: Updater) => void) => Unsubscriber | void, initial_value?: T ): Readable; @@ -163,7 +163,7 @@ export function derived(stores: Stores, fn: Function, initial_value?: T): Rea const auto = fn.length < 2; - return readable(initial_value, (set) => { + return readable(initial_value, (set, update) => { let inited = false; const values = []; @@ -175,7 +175,7 @@ export function derived(stores: Stores, fn: Function, initial_value?: T): Rea return; } cleanup(); - const result = fn(single ? values[0] : values, set); + const result = fn(single ? values[0] : values, set, update); if (auto) { set(result as T); } else { diff --git a/test/store/index.ts b/test/store/index.ts index b6fc5940e111..e49bf0572e08 100644 --- a/test/store/index.ts +++ b/test/store/index.ts @@ -128,6 +128,50 @@ describe('store', () => { assert.deepEqual(values, [0, 1, 2]); }); + it('passes an optional update function', () => { + let running; + let tick; + let add; + + const store = readable(undefined, (set, update) => { + tick = set; + running = true; + add = n => update(value => value + n); + + set(0); + + return () => { + tick = () => { }; + add = _ => { }; + running = false; + }; + }); + + assert.ok(!running); + + const values = []; + + const unsubscribe = store.subscribe(value => { + values.push(value); + }); + + assert.ok(running); + tick(1); + tick(2); + add(3); + add(4); + tick(5); + add(6); + + unsubscribe(); + + assert.ok(!running); + tick(7); + add(8); + + assert.deepEqual(values, [0, 1, 2, 5, 9, 5, 11]); + }); + it('creates an undefined readable store', () => { const store = readable(); const values = []; @@ -231,6 +275,39 @@ describe('store', () => { assert.deepEqual(values, [0, 2, 4]); }); + it('passes optional set and update functions', () => { + const number = writable(1); + const evensAndSquaresOf4 = derived(number, (n, set, update) => { + if (n % 2 === 0) set(n); + if (n % 4 === 0) update(n => n * n); + }, 0); + + const values = []; + + const unsubscribe = evensAndSquaresOf4.subscribe(value => { + values.push(value); + }); + + number.set(2); + number.set(3); + number.set(4); + number.set(5); + number.set(6); + assert.deepEqual(values, [0, 2, 4, 16, 6]); + + number.set(7); + number.set(8); + number.set(9); + number.set(10); + assert.deepEqual(values, [0, 2, 4, 16, 6, 8, 64, 10]); + + unsubscribe(); + + number.set(11); + number.set(12); + assert.deepEqual(values, [0, 2, 4, 16, 6, 8, 64, 10]); + }); + it('prevents glitches', () => { const lastname = writable('Jekyll'); const firstname = derived(lastname, n => n === 'Jekyll' ? 'Henry' : 'Edward');