Skip to content

Commit

Permalink
Merge pull request #2955 from sveltejs/gh-2660
Browse files Browse the repository at this point in the history
solve diamond dependencies
  • Loading branch information
Rich-Harris authored Jun 6, 2019
2 parents 77bb111 + 0d31e5c commit 38330c9
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 6 deletions.
31 changes: 25 additions & 6 deletions src/runtime/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type Unsubscriber = () => void;
type Updater<T> = (value: T) => T;

/** Cleanup logic callback. */
type Invalidater<T> = (value?: T) => void;
type Invalidator<T> = (value?: T) => void;

/** Start and stop notification callbacks. */
type StartStopNotifier<T> = (set: Subscriber<T>) => Unsubscriber | void;
Expand All @@ -22,7 +22,7 @@ export interface Readable<T> {
* @param run subscription callback
* @param invalidate cleanup callback
*/
subscribe(run: Subscriber<T>, invalidate?: Invalidater<T>): Unsubscriber;
subscribe(run: Subscriber<T>, invalidate?: Invalidator<T>): Unsubscriber;
}

/** Writable interface for both updating and subscribing. */
Expand All @@ -41,7 +41,7 @@ export interface Writable<T> extends Readable<T> {
}

/** Pair of subscriber and invalidator. */
type SubscribeInvalidateTuple<T> = [Subscriber<T>, Invalidater<T>];
type SubscribeInvalidateTuple<T> = [Subscriber<T>, Invalidator<T>];

/**
* Creates a `Readable` store that allows reading by subscription.
Expand Down Expand Up @@ -78,7 +78,7 @@ export function writable<T>(value: T, start: StartStopNotifier<T> = noop): Writa
set(fn(value));
}

function subscribe(run: Subscriber<T>, invalidate: Invalidater<T> = noop): Unsubscriber {
function subscribe(run: Subscriber<T>, invalidate: Invalidator<T> = noop): Unsubscriber {
const subscriber: SubscribeInvalidateTuple<T> = [run, invalidate];
subscribers.push(subscriber);
if (subscribers.length === 1) {
Expand Down Expand Up @@ -127,7 +127,9 @@ export function derived<T, S extends Stores>(

const auto = fn.length < 2;

return readable(initial_value, (set) => {
const invalidators: Array<Invalidator<T>> = [];

const store = readable(initial_value, (set) => {
let inited = false;
const values: StoresValues<S> = [] as StoresValues<S>;

Expand Down Expand Up @@ -156,6 +158,7 @@ export function derived<T, S extends Stores>(
}
},
() => {
run_all(invalidators);
pending |= (1 << i);
}),
);
Expand All @@ -168,6 +171,22 @@ export function derived<T, S extends Stores>(
cleanup();
};
});

return {
subscribe(run: Subscriber<T>, invalidate: Invalidator<T> = 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();
};
}
}
}

/**
Expand All @@ -178,4 +197,4 @@ export function get<T>(store: Readable<T>): T {
let value: T | undefined;
store.subscribe((_: T) => value = _)();
return value as T;
}
}
28 changes: 28 additions & 0 deletions test/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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];

Expand Down

0 comments on commit 38330c9

Please sign in to comment.