subscription within a subscription

typescript
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) {
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));
}
@ -31,7 +43,12 @@ const sliceSubscriber = (slice, subdux) => (subscription) => (store) => {
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) => {
@ -67,13 +84,12 @@ export class Updux {
constructor(config) {
this.#initial = config.initial ?? {};
this.#subduxes = config.subduxes ?? {};
if( config.actions ) {
for (const [ type, actionArg ] of Object.entries(config.actions)) {
if( typeof actionArg === 'function' && actionArg.type ) {
if (config.actions) {
for (const [type, actionArg] of Object.entries(config.actions)) {
if (typeof actionArg === 'function' && actionArg.type) {
this.#actions[type] = actionArg;
}
else {
this.#actions[type] = action(type,actionArg)
} else {
this.#actions[type] = action(type, actionArg);
}
}
}
@ -103,16 +119,7 @@ export class Updux {
#memoMiddleware = moize(buildMiddleware);
get subscriptions() {
const subscriptions = [...this.#subscriptions].map((s) =>
memoizeSubscription(s)
);
return [
...subscriptions,
...map(this.#subduxes, (v, k) =>
v.subscriptions.map(sliceSubscriber(k, v))
).flat(),
];
return this.#subscriptions;
}
get middleware() {
@ -186,6 +193,30 @@ export class Updux {
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() {
const store = reduxCreateStore(
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;
}

View File

@ -107,10 +107,7 @@ tap.test('subduxes subscriptions', async (t) => {
});
});
tap.test( "subscription within subduxes", {only: true},async(t) => {
tap.test('subscription within subduxes', { todo: false }, async (t) => {
let innerState = 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,
actions: { inc: null },
mutations: {
inc: () => state => state + 1,
inc: () => (state) => state + 1,
},
subscriptions: [
store => (state, previous, unsub) => {
if(!previous) return;
store.subscribe( innerState );
(store) => (state, previous, unsub) => {
console.log(state, previous);
if (!previous) return;
store.subscribe(innerState);
unsub();
}
},
],
})
});
const dux = new Updux({
initial: { x: 0 },
subduxes: { inner },
actions: {
incOuter: null,
},
mutations: {
incOuter: () => (state) => ({ ...state, x: state.x + 1 }),
},
subscriptions: [
store => (state, previous, unsub) => {
console.log(state,previous);
if(!previous) return;
store.subscribe( outerState );
(store) => (state, previous, unsub) => {
if (!previous) return;
store.subscribe(outerState);
unsub();
}
},
],
});
@ -146,17 +150,39 @@ tap.test( "subscription within subduxes", {only: true},async(t) => {
store.dispatch({ type: 'noop' });
store.dispatch({ type: 'noop' });
t.notOk( innerState.called );
t.notOk( outerState.called );
t.notOk(innerState.called);
t.notOk(outerState.called);
store.dispatch.inc();
// still not called, but waiting, now
t.notOk( innerState.called );
t.notOk( outerState.called );
// not called yet
t.notOk(innerState.called);
t.notOk(outerState.called);
store.dispatch.inc();
console.log(outerState.firstCall.args);
// console.log(outerState.firstCall)
t.ok(outerState.calledOnce);
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);
});
});