splat
This commit is contained in:
parent
912ea85edc
commit
d14eb08bf0
@ -64,7 +64,7 @@
|
||||
"url": "https://github.com/yanick/updux/issues"
|
||||
},
|
||||
"homepage": "https://github.com/yanick/updux#readme",
|
||||
"types": "./dist/index.d.ts",
|
||||
"types": "./types/index.d.ts",
|
||||
"prettier": {
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
|
200
src/Updux.js
200
src/Updux.js
@ -1,7 +1,8 @@
|
||||
/* TODO change * for leftovers to +, change subscriptions to reactions */
|
||||
import moize from 'moize';
|
||||
import u from '@yanick/updeep';
|
||||
import { createStore as reduxCreateStore, applyMiddleware } from 'redux';
|
||||
import { map, mapValues } from 'lodash-es';
|
||||
import { get, map, mapValues, merge, difference } from 'lodash-es';
|
||||
|
||||
import { buildInitial } from './buildInitial/index.js';
|
||||
import { buildActions } from './buildActions/index.js';
|
||||
@ -13,56 +14,6 @@ import {
|
||||
augmentMiddlewareApi,
|
||||
} from './buildMiddleware/index.js';
|
||||
|
||||
function _subscribeToStore(store, subscriptions) {
|
||||
for (const sub of subscriptions) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
const sliceSubscriber = (slice, subdux) => (subscription) => (store) => {
|
||||
let localStore = augmentMiddlewareApi(
|
||||
{
|
||||
...store,
|
||||
getState: () => store.getState()[slice],
|
||||
},
|
||||
subdux.actions,
|
||||
subdux.selectors
|
||||
);
|
||||
|
||||
return (state, previous, unsub) =>
|
||||
subscription(localStore)(
|
||||
state[slice],
|
||||
previous && previous[slice],
|
||||
unsub
|
||||
);
|
||||
};
|
||||
|
||||
const memoizeSubscription = (subscription) => (store) => {
|
||||
let previous = undefined;
|
||||
const subscriber = subscription(store);
|
||||
|
||||
return (state, unsub) => {
|
||||
if (state === previous) return;
|
||||
let p = previous;
|
||||
previous = state;
|
||||
subscriber(state, p, unsub);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* `Updux` is a way to minimize and simplify the boilerplate associated with the
|
||||
@ -71,6 +22,7 @@ const memoizeSubscription = (subscription) => (store) => {
|
||||
* In true `Redux`-like fashion, upduxes can be made of sub-upduxes (`subduxes` for short) for different slices of the root state.
|
||||
*/
|
||||
export class Updux {
|
||||
/** @type { unknown } */
|
||||
#initial = {};
|
||||
#subduxes = {};
|
||||
|
||||
@ -80,10 +32,19 @@ export class Updux {
|
||||
#mutations = {};
|
||||
#effects = [];
|
||||
#subscriptions = [];
|
||||
#splatSelector = undefined;
|
||||
#splatReaction = undefined;
|
||||
|
||||
constructor(config) {
|
||||
this.#initial = config.initial ?? {};
|
||||
this.#subduxes = config.subduxes ?? {};
|
||||
|
||||
if (config.subduxes) {
|
||||
this.#subduxes = mapValues(config.subduxes, (sub) =>
|
||||
sub instanceof Updux ? sub : new Updux(sub)
|
||||
);
|
||||
}
|
||||
|
||||
if (config.actions) {
|
||||
for (const [type, actionArg] of Object.entries(config.actions)) {
|
||||
if (typeof actionArg === 'function' && actionArg.type) {
|
||||
@ -98,6 +59,8 @@ export class Updux {
|
||||
|
||||
this.#mutations = config.mutations ?? {};
|
||||
|
||||
this.#splatSelector = config.splatSelector;
|
||||
|
||||
Object.keys(this.#mutations)
|
||||
.filter((action) => action !== '*')
|
||||
.filter((action) => !this.actions.hasOwnProperty(action))
|
||||
@ -110,6 +73,8 @@ export class Updux {
|
||||
}
|
||||
|
||||
this.#subscriptions = config.subscriptions ?? [];
|
||||
|
||||
this.#splatReaction = config.splatReaction;
|
||||
}
|
||||
|
||||
#memoInitial = moize(buildInitial);
|
||||
@ -122,6 +87,10 @@ export class Updux {
|
||||
return this.#subscriptions;
|
||||
}
|
||||
|
||||
setSplatSelector(name, f) {
|
||||
this.#splatSelector = [name, f];
|
||||
}
|
||||
|
||||
get middleware() {
|
||||
return this.#memoMiddleware(
|
||||
this.#effects,
|
||||
@ -131,6 +100,7 @@ export class Updux {
|
||||
);
|
||||
}
|
||||
|
||||
/** @return { import('./Updux').What } */
|
||||
get initial() {
|
||||
return this.#memoInitial(this.#initial, this.#subduxes);
|
||||
}
|
||||
@ -143,7 +113,11 @@ export class Updux {
|
||||
}
|
||||
|
||||
get selectors() {
|
||||
return this.#memoSelectors(this.#selectors, this.#subduxes);
|
||||
return this.#memoSelectors(
|
||||
this.#selectors,
|
||||
this.#splatSelector,
|
||||
this.#subduxes
|
||||
);
|
||||
}
|
||||
|
||||
get upreducer() {
|
||||
@ -193,7 +167,49 @@ export class Updux {
|
||||
this.#effects = [...this.#effects, [action, effect]];
|
||||
}
|
||||
|
||||
subscribeTo(store, subscription) {
|
||||
splatSubscriber(store, inner, splatReaction) {
|
||||
const cache = {};
|
||||
|
||||
return () => (state, previous, unsub) => {
|
||||
const cacheKeys = Object.keys(cache);
|
||||
|
||||
const newKeys = difference(Object.keys(state), cacheKeys);
|
||||
|
||||
for (const slice of newKeys) {
|
||||
let localStore = {
|
||||
...store,
|
||||
getState: () => store.getState()[slice],
|
||||
};
|
||||
|
||||
cache[slice] = [];
|
||||
|
||||
if (typeof splatReaction === 'function') {
|
||||
localStore = {
|
||||
...localStore,
|
||||
...splatReaction(localStore, slice),
|
||||
};
|
||||
}
|
||||
|
||||
const { unsub, subscriber, subscriberRaw } =
|
||||
inner.subscribeAll(localStore);
|
||||
|
||||
cache[slice].push({ unsub, subscriber, subscriberRaw });
|
||||
subscriber();
|
||||
}
|
||||
|
||||
const deletedKeys = difference(cacheKeys, Object.keys(state));
|
||||
|
||||
for (const deleted of deletedKeys) {
|
||||
for (const inner of cache[deleted]) {
|
||||
inner.subscriber();
|
||||
inner.unsub();
|
||||
}
|
||||
delete cache[deleted];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
subscribeTo(store, subscription, setupArgs = []) {
|
||||
const localStore = augmentMiddlewareApi(
|
||||
{
|
||||
...store,
|
||||
@ -204,29 +220,77 @@ export class Updux {
|
||||
this.selectors
|
||||
);
|
||||
|
||||
const subscriber = subscription(localStore);
|
||||
const subscriber = subscription(localStore, ...setupArgs);
|
||||
|
||||
let previous;
|
||||
|
||||
const unsub = store.subscribe(() => {
|
||||
const memoSub = () => {
|
||||
const state = store.getState();
|
||||
if (state === previous) return;
|
||||
let p = previous;
|
||||
previous = state;
|
||||
subscriber(state, p, unsub);
|
||||
});
|
||||
};
|
||||
|
||||
let ret = store.subscribe(memoSub);
|
||||
const unsub = typeof ret === 'function' ? ret : ret.unsub;
|
||||
return {
|
||||
unsub,
|
||||
subscriber: memoSub,
|
||||
subscriberRaw: subscriber,
|
||||
};
|
||||
}
|
||||
|
||||
createStore() {
|
||||
subscribeAll(store) {
|
||||
let results = this.#subscriptions.map((sub) =>
|
||||
this.subscribeTo(store, sub)
|
||||
);
|
||||
|
||||
for (const subdux in this.#subduxes) {
|
||||
if (subdux !== '*') {
|
||||
const localStore = {
|
||||
...store,
|
||||
getState: () => get(store.getState(), subdux),
|
||||
};
|
||||
results.push(this.#subduxes[subdux].subscribeAll(localStore));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.#splatReaction) {
|
||||
results.push(
|
||||
this.subscribeTo(
|
||||
store,
|
||||
this.splatSubscriber(
|
||||
store,
|
||||
this.#subduxes['*'],
|
||||
this.#splatReaction
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
unsub: () => results.forEach(({ unsub }) => unsub()),
|
||||
subscriber: () => results.forEach(({ subscriber }) => subscriber()),
|
||||
subscriberRaw: (...args) =>
|
||||
results.forEach(({ subscriberRaw }) => subscriberRaw(...args)),
|
||||
};
|
||||
}
|
||||
|
||||
createStore(initial) {
|
||||
const store = reduxCreateStore(
|
||||
this.reducer,
|
||||
this.initial,
|
||||
initial ?? this.initial,
|
||||
applyMiddleware(this.middleware)
|
||||
);
|
||||
|
||||
store.actions = this.actions;
|
||||
|
||||
store.selectors = mapValues(this.selectors, (selector) => {
|
||||
store.selectors = this.selectors;
|
||||
|
||||
merge(
|
||||
store.getState,
|
||||
mapValues(this.selectors, (selector) => {
|
||||
return (...args) => {
|
||||
let result = selector(store.getState());
|
||||
|
||||
@ -234,7 +298,8 @@ export class Updux {
|
||||
|
||||
return result;
|
||||
};
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
for (const action in this.actions) {
|
||||
store.dispatch[action] = (...args) => {
|
||||
@ -242,18 +307,11 @@ export class Updux {
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
this.subscribeAll(store);
|
||||
|
||||
return store;
|
||||
}
|
||||
}
|
||||
|
||||
const x = new Updux();
|
||||
x.selectors;
|
||||
|
@ -74,7 +74,7 @@ test('basic selectors', async (t) => {
|
||||
getBar: ({ bar }) => bar,
|
||||
},
|
||||
});
|
||||
dux.addSelector('getFoo', (state) => state.foo);
|
||||
dux.addSelector('getFoo', ({ foo }) => foo);
|
||||
dux.addSelector(
|
||||
'getAdd',
|
||||
({ foo }) =>
|
||||
@ -91,9 +91,9 @@ test('basic selectors', async (t) => {
|
||||
|
||||
const store = dux.createStore();
|
||||
|
||||
t.equal(store.selectors.getFoo(), 1);
|
||||
t.equal(store.selectors.getQuux(), 3);
|
||||
t.equal(store.selectors.getAdd(7), 8);
|
||||
t.equal(store.getState.getFoo(), 1);
|
||||
t.equal(store.getState.getQuux(), 3);
|
||||
t.equal(store.getState.getAdd(7), 8);
|
||||
});
|
||||
|
||||
test('mutations', async (t) => {
|
||||
|
@ -7,6 +7,8 @@ export function buildInitial(initial, subduxes = {}) {
|
||||
"can't have subduxes on a dux which state is not an object"
|
||||
);
|
||||
|
||||
if (Object.keys(subduxes).length === 1 && subduxes['*']) return initial;
|
||||
|
||||
const subInitial = mapValues(subduxes, ({ initial }, key) =>
|
||||
key === '*' ? [] : initial
|
||||
);
|
||||
|
@ -69,7 +69,7 @@ export function buildMiddleware(
|
||||
sub = {}
|
||||
) {
|
||||
let inner = map(sub, ({ middleware }, slice) =>
|
||||
middleware ? sliceMw(slice, middleware) : undefined
|
||||
slice !== '*' && middleware ? sliceMw(slice, middleware) : undefined
|
||||
).filter((x) => x);
|
||||
|
||||
const local = effects.map((effect) =>
|
||||
|
@ -1,11 +1,30 @@
|
||||
import { map, mapValues, merge } from 'lodash-es';
|
||||
|
||||
export function buildSelectors(localSelectors, subduxes) {
|
||||
export function buildSelectors(localSelectors, splatSelector, subduxes) {
|
||||
const subSelectors = map(subduxes, ({ selectors }, slice) => {
|
||||
if (!selectors) return {};
|
||||
if (slice === '*') return {};
|
||||
|
||||
return mapValues(selectors, (func) => (state) => func(state[slice]));
|
||||
});
|
||||
|
||||
return merge({}, ...subSelectors, localSelectors);
|
||||
let splat = {};
|
||||
if (splatSelector) {
|
||||
splat[splatSelector[0]] =
|
||||
(state) =>
|
||||
(...args) => {
|
||||
const value = splatSelector[1](state)(...args);
|
||||
|
||||
const res = () => value;
|
||||
return merge(
|
||||
res,
|
||||
mapValues(
|
||||
subduxes['*'].selectors,
|
||||
(selector) => () => selector(value)
|
||||
)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
return merge({}, ...subSelectors, localSelectors, splat);
|
||||
}
|
||||
|
@ -11,12 +11,20 @@ export function buildUpreducer(initial, mutations, subduxes = {}) {
|
||||
let newState = state ?? initial;
|
||||
|
||||
if (subReducers) {
|
||||
if (subduxes['*']) {
|
||||
newState = u.updateIn(
|
||||
'*',
|
||||
subduxes['*'].upreducer(action),
|
||||
newState
|
||||
);
|
||||
} else {
|
||||
const update = mapValues(subReducers, (upReducer) =>
|
||||
upReducer(action)
|
||||
);
|
||||
|
||||
newState = u(update, newState);
|
||||
}
|
||||
}
|
||||
|
||||
const a = mutations[action.type] || mutations['*'];
|
||||
|
||||
|
314
src/splat.test.js
Normal file
314
src/splat.test.js
Normal file
@ -0,0 +1,314 @@
|
||||
import { test } from 'tap';
|
||||
import sinon from 'sinon';
|
||||
import { difference, omit } from 'lodash-es';
|
||||
|
||||
import { Updux } from './Updux.js';
|
||||
|
||||
test('initial', async (t) => {
|
||||
const dux = new Updux({
|
||||
initial: {},
|
||||
subduxes: {
|
||||
'*': {
|
||||
initial: { a: 1 },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
t.same(dux.initial, {});
|
||||
});
|
||||
|
||||
test('actions', async (t) => {
|
||||
const dux = new Updux({
|
||||
initial: {},
|
||||
subduxes: {
|
||||
'*': {
|
||||
initial: { a: 1 },
|
||||
actions: { foo: null },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
t.type(dux.actions.foo, 'function');
|
||||
});
|
||||
|
||||
test('selectors', async (t) => {
|
||||
const dux = new Updux({
|
||||
initial: {},
|
||||
subduxes: {
|
||||
'*': {
|
||||
initial: { a: 1 },
|
||||
selectors: {
|
||||
getA: ({ a }) => a,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
t.same(dux.selectors, {});
|
||||
|
||||
const getInner = (state) => (index) => state[index];
|
||||
dux.setSplatSelector('getInner', getInner);
|
||||
|
||||
t.type(dux.selectors.getInner, 'function');
|
||||
|
||||
t.same(
|
||||
dux.selectors.getInner({
|
||||
one: { a: 1 },
|
||||
two: { a: 2 },
|
||||
})('one')(),
|
||||
{ a: 1 }
|
||||
);
|
||||
|
||||
t.same(
|
||||
dux.selectors
|
||||
.getInner({
|
||||
one: { a: 1 },
|
||||
two: { a: 2 },
|
||||
})('one')
|
||||
.getA(),
|
||||
1
|
||||
);
|
||||
|
||||
// and now with the store
|
||||
const store = dux.createStore({
|
||||
one: { a: 1 },
|
||||
two: { a: 2 },
|
||||
});
|
||||
|
||||
t.same(store.getState.getInner('two')(), { a: 2 });
|
||||
t.same(store.getState.getInner('two').getA(), 2);
|
||||
});
|
||||
|
||||
test('splat middleware', async (t) => {
|
||||
const snitch = sinon.fake(() => true);
|
||||
|
||||
const dux = new Updux({
|
||||
initial: {},
|
||||
subduxes: {
|
||||
'*': {
|
||||
initial: { a: 1 },
|
||||
actions: { foo: null },
|
||||
effects: {
|
||||
foo: (api) => (next) => (action) => {
|
||||
snitch();
|
||||
next(action);
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const store = dux.createStore({ one: { a: 1 }, two: { a: 2 } });
|
||||
store.dispatch.foo();
|
||||
|
||||
t.notOk(snitch.called);
|
||||
});
|
||||
|
||||
test('splat subscriptions', async (t) => {
|
||||
const snitch = sinon.fake(() => true);
|
||||
|
||||
const dux = new Updux({
|
||||
initial: {},
|
||||
subduxes: {
|
||||
'*': {
|
||||
initial: { a: 1 },
|
||||
actions: { foo: null },
|
||||
mutations: {
|
||||
foo: (id) => (state) =>
|
||||
state.a === i ? { ...state, b: 1 } : state,
|
||||
},
|
||||
subscriptions: [
|
||||
(store) => (state, previous, unsub) => {
|
||||
snitch(state);
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const store = dux.createStore({ one: { a: 1 }, two: { a: 2 } });
|
||||
store.dispatch({ type: 'noop' });
|
||||
|
||||
t.notOk(snitch.called);
|
||||
});
|
||||
|
||||
test('splat subscriptions, more', async (t) => {
|
||||
const snitch = sinon.fake(() => true);
|
||||
|
||||
const inner = new Updux({
|
||||
initial: { a: 1 },
|
||||
actions: { foo: null, incAll: null },
|
||||
mutations: {
|
||||
foo: (id) => (state) => state.a === id ? { ...state, b: 1 } : state,
|
||||
incAll: () => (state) => ({ ...state, a: state.a + 1 }),
|
||||
},
|
||||
subscriptions: [() => snitch],
|
||||
});
|
||||
|
||||
const dux = new Updux({
|
||||
initial: {},
|
||||
actions: {
|
||||
delete: null,
|
||||
newEntry: null,
|
||||
},
|
||||
mutations: {
|
||||
delete: (id) => (state) => omit(state, id),
|
||||
newEntry: (name) => (state) => ({ ...state, [name]: { a: 10 } }),
|
||||
},
|
||||
splatReaction: 'whatev',
|
||||
subduxes: {
|
||||
'*': inner,
|
||||
},
|
||||
});
|
||||
|
||||
const store = dux.createStore({ one: { a: 1 }, two: { a: 2 } });
|
||||
store.dispatch({ type: 'noop' });
|
||||
|
||||
t.equal(snitch.callCount, 2);
|
||||
|
||||
t.same(snitch.firstCall.args[0], { a: 1 });
|
||||
t.same(snitch.secondCall.args[0], { a: 2 });
|
||||
|
||||
snitch.resetHistory();
|
||||
|
||||
store.dispatch.foo(2);
|
||||
|
||||
t.equal(snitch.callCount, 1);
|
||||
|
||||
t.same(snitch.firstCall.args[0], { a: 2, b: 1 });
|
||||
|
||||
snitch.resetHistory();
|
||||
store.dispatch.delete('one');
|
||||
|
||||
t.same(store.getState(), {
|
||||
two: { a: 2, b: 1 },
|
||||
});
|
||||
|
||||
t.equal(snitch.callCount, 1);
|
||||
|
||||
t.ok(
|
||||
snitch.calledWithMatch(
|
||||
undefined,
|
||||
{ a: 1 },
|
||||
sinon.match.typeOf('function')
|
||||
)
|
||||
);
|
||||
|
||||
await t.test('only one subscriber left', async (t) => {
|
||||
snitch.resetHistory();
|
||||
store.dispatch.incAll();
|
||||
|
||||
t.equal(snitch.callCount, 1);
|
||||
|
||||
t.ok(
|
||||
snitch.calledWithMatch(
|
||||
{ a: 3, b: 1 },
|
||||
{ a: 2, b: 1 },
|
||||
sinon.match.typeOf('function')
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
await t.test('new entry gets subscribed', async (t) => {
|
||||
snitch.resetHistory();
|
||||
store.dispatch.newEntry('newbie');
|
||||
|
||||
t.equal(snitch.callCount, 1);
|
||||
|
||||
t.ok(
|
||||
snitch.calledWithMatch(
|
||||
{ a: 10 },
|
||||
undefined,
|
||||
sinon.match.typeOf('function')
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('many levels down', { only: false }, async (t) => {
|
||||
const snitch = sinon.fake(() => true);
|
||||
|
||||
const dux = new Updux({
|
||||
splatReaction: 'potato',
|
||||
actions: { remove: null },
|
||||
mutations: {
|
||||
remove: () => (state) => ({}),
|
||||
},
|
||||
subduxes: {
|
||||
'*': {
|
||||
subduxes: {
|
||||
a: {
|
||||
initial: 1,
|
||||
actions: {
|
||||
add: null,
|
||||
},
|
||||
mutations: {
|
||||
add: () => (x) => x + 1,
|
||||
},
|
||||
subscriptions: [
|
||||
(store) => (state) => snitch(state, store),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const store = dux.createStore({ one: { a: 1 } });
|
||||
store.dispatch({ type: 'foo' });
|
||||
|
||||
t.ok(snitch.calledOnce);
|
||||
t.same(snitch.firstCall.firstArg, 1);
|
||||
|
||||
snitch.resetHistory();
|
||||
|
||||
store.dispatch.remove();
|
||||
|
||||
t.ok(snitch.calledOnce);
|
||||
t.same(snitch.firstCall.firstArg, undefined);
|
||||
});
|
||||
|
||||
test('inherit info via the store', async (t) => {
|
||||
const snitch = sinon.fake(() => true);
|
||||
const splatSnitch = sinon.fake(() => true);
|
||||
|
||||
const dux = new Updux({
|
||||
splatReaction: (store, key) => {
|
||||
store.itemId = key;
|
||||
|
||||
splatSnitch();
|
||||
return { itemId: key };
|
||||
},
|
||||
subduxes: {
|
||||
'*': {
|
||||
subduxes: {
|
||||
a: {
|
||||
initial: 1,
|
||||
actions: {
|
||||
add: null,
|
||||
},
|
||||
mutations: {
|
||||
add: () => (x) => x + 1,
|
||||
},
|
||||
subscriptions: [
|
||||
(store) => (state) => snitch(state, store.itemId),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const store = dux.createStore({
|
||||
one: { a: 1 },
|
||||
two: { a: 2 },
|
||||
});
|
||||
|
||||
store.dispatch({ type: 'noop' });
|
||||
|
||||
t.ok(splatSnitch.calledTwice);
|
||||
|
||||
t.ok(snitch.calledTwice);
|
||||
t.ok(snitch.calledWithMatch(1, 'one'));
|
||||
t.ok(snitch.calledWithMatch(2, 'two'));
|
||||
});
|
@ -119,7 +119,6 @@ tap.test('subscription within subduxes', { todo: false }, async (t) => {
|
||||
},
|
||||
subscriptions: [
|
||||
(store) => (state, previous, unsub) => {
|
||||
console.log(state, previous);
|
||||
if (!previous) return;
|
||||
store.subscribe(innerState);
|
||||
unsub();
|
||||
|
Loading…
Reference in New Issue
Block a user