Skip to content

Commit a2ff93c

Browse files
committed
glitch-free reactive stores
1 parent 8d4e514 commit a2ff93c

File tree

2 files changed

+52
-16
lines changed

2 files changed

+52
-16
lines changed

store.js

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { run_all } from './internal.js';
1+
import { run_all, noop } from './internal.js';
22

33
export function readable(start, value) {
44
const subscribers = [];
@@ -7,20 +7,22 @@ export function readable(start, value) {
77
function set(newValue) {
88
if (newValue === value) return;
99
value = newValue;
10-
subscribers.forEach(fn => fn(value));
10+
subscribers.forEach(s => s[1]());
11+
subscribers.forEach(s => s[0](value));
1112
}
1213

1314
return {
14-
subscribe(fn) {
15+
subscribe(run, invalidate = noop) {
1516
if (subscribers.length === 0) {
1617
stop = start(set);
1718
}
1819

19-
subscribers.push(fn);
20-
fn(value);
20+
const subscriber = [run, invalidate];
21+
subscribers.push(subscriber);
22+
run(value);
2123

2224
return function() {
23-
const index = subscribers.indexOf(fn);
25+
const index = subscribers.indexOf(subscriber);
2426
if (index !== -1) subscribers.splice(index, 1);
2527

2628
if (subscribers.length === 0) {
@@ -38,19 +40,21 @@ export function writable(value) {
3840
function set(newValue) {
3941
if (newValue === value) return;
4042
value = newValue;
41-
subscribers.forEach(fn => fn(value));
43+
subscribers.forEach(s => s[1]());
44+
subscribers.forEach(s => s[0](value));
4245
}
4346

4447
function update(fn) {
4548
set(fn(value));
4649
}
4750

48-
function subscribe(fn) {
49-
subscribers.push(fn);
50-
fn(value);
51+
function subscribe(run, invalidate = noop) {
52+
const subscriber = [run, invalidate];
53+
subscribers.push(subscriber);
54+
run(value);
5155

5256
return () => {
53-
const index = subscribers.indexOf(fn);
57+
const index = subscribers.indexOf(subscriber);
5458
if (index !== -1) subscribers.splice(index, 1);
5559
};
5660
}
@@ -63,20 +67,30 @@ export function derive(stores, fn) {
6367
if (single) stores = [stores];
6468

6569
const auto = fn.length === 1;
70+
let value = {};
6671

6772
return readable(set => {
6873
let inited = false;
6974
const values = [];
7075

76+
let pending = 0;
77+
7178
const sync = () => {
79+
if (pending) return;
7280
const result = fn(single ? values[0] : values, set);
73-
if (auto) set(result);
81+
if (auto && (value !== (value = result))) set(result);
7482
}
7583

76-
const unsubscribers = stores.map((store, i) => store.subscribe(value => {
77-
values[i] = value;
78-
if (inited) sync();
79-
}));
84+
const unsubscribers = stores.map((store, i) => store.subscribe(
85+
value => {
86+
values[i] = value;
87+
pending &= ~(1 << i);
88+
if (inited) sync();
89+
},
90+
() => {
91+
pending |= (1 << i);
92+
})
93+
);
8094

8195
inited = true;
8296
sync();

test/store/index.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,5 +128,27 @@ describe('store', () => {
128128
number.set(7);
129129
assert.deepEqual(values, [0, 2, 4]);
130130
});
131+
132+
it('prevents glitches', () => {
133+
const lastname = writable('Jekyll');
134+
const firstname = derive(lastname, n => n === 'Jekyll' ? 'Henry' : 'Edward');
135+
136+
const fullname = derive([firstname, lastname], names => names.join(' '));
137+
138+
const values = [];
139+
140+
const unsubscribe = fullname.subscribe(value => {
141+
values.push(value);
142+
});
143+
144+
lastname.set('Hyde');
145+
146+
assert.deepEqual(values, [
147+
'Henry Jekyll',
148+
'Edward Hyde'
149+
]);
150+
151+
unsubscribe();
152+
});
131153
});
132154
});

0 commit comments

Comments
 (0)