Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions packages/svelte/src/runtime/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export function writable(value, start = noop) {
* @template T
* @overload
* @param {S} stores - input stores
* @param {(values: import('./public.js').StoresValues<S>, set: import('./public.js').Subscriber<T>, update: (fn: import('./public.js').Updater<T>) => void) => import('./public.js').Unsubscriber | void} fn - function callback that aggregates the values
* @param {(values: import('./public.js').StoresValues<S>, set: import('./public.js').Subscriber<T>, update: (fn: import('./public.js').Updater<T>) => void, changed: boolean[]) => import('./public.js').Unsubscriber | void} fn - function callback that aggregates the values
* @param {T} [initial_value] - initial value
* @returns {import('./public.js').Readable<T>}
*/
Expand Down Expand Up @@ -133,13 +133,15 @@ export function derived(stores, fn, initial_value) {
let started = false;
const values = [];
let pending = 0;
const changed = [];
let cleanup = noop;
const sync = () => {
if (pending) {
return;
}
cleanup();
const result = fn(single ? values[0] : values, set, update);
const result = fn(single ? values[0] : values, set, update, changed);
changed.fill(false);
if (auto) {
set(result);
} else {
Expand All @@ -152,6 +154,7 @@ export function derived(stores, fn, initial_value) {
(value) => {
values[i] = value;
pending &= ~(1 << i);
changed[i] = true;
if (started) {
sync();
}
Expand Down
39 changes: 39 additions & 0 deletions packages/svelte/test/store/store.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,45 @@ describe('store', () => {
unsubscribe();
});

it('provides a boolean array to easily tell what changed', () => {
const count = writable(0);
const values = [];

const a = derived(count, ($count) => {
return 'a' + $count;
});

const b = derived(count, ($count) => {
return 'b' + $count;
});

const c = writable(0);

const combined = derived([a, b, c], ([a, b, c], set, _u, changes) => {
const [aChanged, bChanged, cChanged] = changes;
if (aChanged && bChanged) {
set(a + b);
} else if (cChanged) {
set('c' + c);
} else {
set('a or b changed without the other one changing');
}
});

const unsubscribe = combined.subscribe((v) => {
values.push(v);
});

assert.deepEqual(values, ['a0b0']);

c.set(2);
count.set(1);

assert.deepEqual(values, ['a0b0', 'c2', 'a1b1']);

unsubscribe();
});

it('derived dependency does not update and shared ancestor updates', () => {
const root = writable({ a: 0, b: 0 });
const values = [];
Expand Down
17 changes: 15 additions & 2 deletions site/content/docs/04-run-time.md
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ store = derived(a, callback: (a: any, set: (value: any) => void, update: (fn: an
store = derived([a, ...b], callback: ([a: any, ...b: any[]]) => any)
```
```js
store = derived([a, ...b], callback: ([a: any, ...b: any[]], set: (value: any) => void, update: (fn: any => any) => void) => void | () => void, initial_value: any)
store = derived([a, ...b], callback: ([a: any, ...b: any[]], set: (value: any) => void, update: (fn: any => any) => void, changed: boolean[]) => void | () => void, initial_value: any)
```

---
Expand Down Expand Up @@ -439,7 +439,7 @@ const tick = derived(frequency, ($frequency, set) => {

---

In both cases, an array of arguments can be passed as the first argument instead of a single store.
In both cases, an array of arguments can be passed as the first argument instead of a single store. In this case, the callback can optionally take a fourth argument, `changed`, which will be an array of Booleans describing which store value(s) changed since the last time the callback was called. (In the single-store case, the `changed` array would be pointless since it would always be equal to `[true]`.)

```js
import { derived } from 'svelte/store';
Expand All @@ -449,6 +449,19 @@ const summed = derived([a, b], ([$a, $b]) => $a + $b);
const delayed = derived([a, b], ([$a, $b], set) => {
setTimeout(() => set($a + $b), 1000);
});

const loggingSum = derived([a, b], ([$a, $b], set, _, changed) => {
const [aChanged, bChanged] = changed;
if (aChanged) console.log('New value of a', $a);
if (bChanged) console.log('New value of b', $b);
set($a + $b);
});

const complexLogic = derived([a, b], ([$a, $b], set, update, changed) => {
const [aChanged, bChanged] = changed;
if (aChanged) set($a + $b);
if (bChanged) update(n => n * 2 - $b);
});
```

#### `get`
Expand Down