subscription within a subscription

This commit is contained in:
Yanick Champoux 2021-10-09 13:15:35 -04:00
parent c1c1edf588
commit 912ea85edc
2 changed files with 109 additions and 42 deletions

View File

@ -15,7 +15,19 @@ import {
function _subscribeToStore(store, subscriptions) { function _subscribeToStore(store, subscriptions) {
for (const sub of subscriptions) { for (const sub of subscriptions) {
const subscriber = sub(store); const subscriber = sub({
...store,
subscribe(subscriber) {
let previous;
const unsub = store.subscribe(() => {
const state = store.getState();
if (state === previous) return;
let p = previous;
previous = state;
subscriber(state, p, unsub);
});
},
});
let unsub = store.subscribe(() => subscriber(store.getState(), unsub)); let unsub = store.subscribe(() => subscriber(store.getState(), unsub));
} }
@ -31,7 +43,12 @@ const sliceSubscriber = (slice, subdux) => (subscription) => (store) => {
subdux.selectors subdux.selectors
); );
return (state, unsub) => subscription(localStore)(state[slice], unsub); return (state, previous, unsub) =>
subscription(localStore)(
state[slice],
previous && previous[slice],
unsub
);
}; };
const memoizeSubscription = (subscription) => (store) => { const memoizeSubscription = (subscription) => (store) => {
@ -71,9 +88,8 @@ export class Updux {
for (const [type, actionArg] of Object.entries(config.actions)) { for (const [type, actionArg] of Object.entries(config.actions)) {
if (typeof actionArg === 'function' && actionArg.type) { if (typeof actionArg === 'function' && actionArg.type) {
this.#actions[type] = actionArg; this.#actions[type] = actionArg;
} } else {
else { this.#actions[type] = action(type, actionArg);
this.#actions[type] = action(type,actionArg)
} }
} }
} }
@ -103,16 +119,7 @@ export class Updux {
#memoMiddleware = moize(buildMiddleware); #memoMiddleware = moize(buildMiddleware);
get subscriptions() { get subscriptions() {
const subscriptions = [...this.#subscriptions].map((s) => return this.#subscriptions;
memoizeSubscription(s)
);
return [
...subscriptions,
...map(this.#subduxes, (v, k) =>
v.subscriptions.map(sliceSubscriber(k, v))
).flat(),
];
} }
get middleware() { get middleware() {
@ -186,6 +193,30 @@ export class Updux {
this.#effects = [...this.#effects, [action, effect]]; this.#effects = [...this.#effects, [action, effect]];
} }
subscribeTo(store, subscription) {
const localStore = augmentMiddlewareApi(
{
...store,
subscribe: (subscriber) =>
this.subscribeTo(store, () => subscriber),
},
this.actions,
this.selectors
);
const subscriber = subscription(localStore);
let previous;
const unsub = store.subscribe(() => {
const state = store.getState();
if (state === previous) return;
let p = previous;
previous = state;
subscriber(state, p, unsub);
});
}
createStore() { createStore() {
const store = reduxCreateStore( const store = reduxCreateStore(
this.reducer, this.reducer,
@ -211,7 +242,17 @@ export class Updux {
}; };
} }
_subscribeToStore(store, this.subscriptions); this.#subscriptions.forEach((sub) => this.subscribeTo(store, sub));
for (const subdux in this.#subduxes) {
const localStore = {
...store,
getState: () => store.getState()[subdux],
};
for (const subscription of this.#subduxes[subdux].subscriptions) {
this.#subduxes[subdux].subscribeTo(localStore, subscription);
}
}
return store; return store;
} }

View File

@ -107,10 +107,7 @@ tap.test('subduxes subscriptions', async (t) => {
}); });
}); });
tap.test('subscription within subduxes', { todo: false }, async (t) => {
tap.test( "subscription within subduxes", {only: true},async(t) => {
let innerState = sinon.fake.returns(null); let innerState = sinon.fake.returns(null);
let outerState = sinon.fake.returns(null); let outerState = sinon.fake.returns(null);
@ -118,26 +115,33 @@ tap.test( "subscription within subduxes", {only: true},async(t) => {
initial: 1, initial: 1,
actions: { inc: null }, actions: { inc: null },
mutations: { mutations: {
inc: () => state => state + 1, inc: () => (state) => state + 1,
}, },
subscriptions: [ subscriptions: [
store => (state, previous, unsub) => { (store) => (state, previous, unsub) => {
console.log(state, previous);
if (!previous) return; if (!previous) return;
store.subscribe(innerState); store.subscribe(innerState);
unsub(); unsub();
} },
], ],
}) });
const dux = new Updux({ const dux = new Updux({
initial: { x: 0 },
subduxes: { inner }, subduxes: { inner },
actions: {
incOuter: null,
},
mutations: {
incOuter: () => (state) => ({ ...state, x: state.x + 1 }),
},
subscriptions: [ subscriptions: [
store => (state, previous, unsub) => { (store) => (state, previous, unsub) => {
console.log(state,previous);
if (!previous) return; if (!previous) return;
store.subscribe(outerState); store.subscribe(outerState);
unsub(); unsub();
} },
], ],
}); });
@ -150,13 +154,35 @@ tap.test( "subscription within subduxes", {only: true},async(t) => {
t.notOk(outerState.called); t.notOk(outerState.called);
store.dispatch.inc(); store.dispatch.inc();
// still not called, but waiting, now
// not called yet
t.notOk(innerState.called); t.notOk(innerState.called);
t.notOk(outerState.called); t.notOk(outerState.called);
store.dispatch.inc(); store.dispatch.inc();
console.log(outerState.firstCall.args); t.ok(outerState.calledOnce);
// console.log(outerState.firstCall) t.same(outerState.firstCall.args[0], { inner: 3, x: 0 });
t.ok(innerState.calledOnce);
t.same(innerState.firstCall.args[0], 3);
innerState.resetHistory();
outerState.resetHistory();
store.dispatch.inc();
t.ok(outerState.calledOnce);
t.same(outerState.firstCall.args[0], { inner: 4, x: 0 });
t.ok(innerState.calledOnce);
t.same(innerState.firstCall.args[0], 4);
await t.test('state of subdux doesnt change', async (t) => {
innerState.resetHistory();
outerState.resetHistory();
store.dispatch.incOuter();
t.ok(outerState.calledOnce);
t.notOk(innerState.called);
});
}); });