middleware work
This commit is contained in:
parent
64e89dc046
commit
332a1cdec7
24
src/actions.test.js
Normal file
24
src/actions.test.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import updux from '.';
|
||||||
|
import u from 'updeep';
|
||||||
|
|
||||||
|
test( 'actions defined in effects and mutations, multi-level', () => {
|
||||||
|
|
||||||
|
const { actions } = updux({
|
||||||
|
effects: {
|
||||||
|
foo: api => next => action => { },
|
||||||
|
},
|
||||||
|
mutations: { bar: () => () => null },
|
||||||
|
subduxes: {
|
||||||
|
mysub: updux({
|
||||||
|
effects: { baz: api => next => action => { }, },
|
||||||
|
mutations: { quux: () => () => null },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const types = Object.keys(actions);
|
||||||
|
types.sort();
|
||||||
|
|
||||||
|
expect( types).toEqual([ 'bar', 'baz', 'foo', 'quux', ]);
|
||||||
|
|
||||||
|
});
|
29
src/buildMiddleware.js
Normal file
29
src/buildMiddleware.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import fp from 'lodash/fp';
|
||||||
|
|
||||||
|
const MiddlewareFor = (type,mw) => api => next => action => {
|
||||||
|
if (type !== '*' && action.type !== type) return next(action);
|
||||||
|
|
||||||
|
return mw(api)(next)(action);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function buildMiddleware(
|
||||||
|
{effects = {}, subduxes = {}},
|
||||||
|
{actions},
|
||||||
|
) {
|
||||||
|
return api => {
|
||||||
|
for (let type in actions) {
|
||||||
|
api.dispatch[type] = (...args) => api.dispatch(actions[type](...args));
|
||||||
|
}
|
||||||
|
|
||||||
|
return original_next => {
|
||||||
|
return [
|
||||||
|
...fp.toPairs(effects).map(([type, effect]) =>
|
||||||
|
MiddlewareFor(type,effect)
|
||||||
|
),
|
||||||
|
...fp.map('middleware', subduxes),
|
||||||
|
]
|
||||||
|
.filter(x => x)
|
||||||
|
.reduceRight((next, mw) => mw(api)(next), original_next);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
37
src/index.js
37
src/index.js
@ -3,6 +3,8 @@ import u from 'updeep';
|
|||||||
|
|
||||||
import { createStore, applyMiddleware } from 'redux';
|
import { createStore, applyMiddleware } from 'redux';
|
||||||
|
|
||||||
|
import buildMiddleware from './buildMiddleware';
|
||||||
|
|
||||||
function actionFor(type) {
|
function actionFor(type) {
|
||||||
return (payload = null, meta = null) => {
|
return (payload = null, meta = null) => {
|
||||||
return fp.pickBy(v => v !== null)({type, payload, meta});
|
return fp.pickBy(v => v !== null)({type, payload, meta});
|
||||||
@ -22,7 +24,7 @@ function buildInitial({initial = {}, subduxes = {}}) {
|
|||||||
return initial;
|
return initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildActions({mutations = {}, subduxes = {}}) {
|
function buildActions({mutations = {}, effects = {}, subduxes = {}}) {
|
||||||
let actions = fp.mergeAll(fp.map(fp.getOr({}, 'actions'), subduxes)) || {};
|
let actions = fp.mergeAll(fp.map(fp.getOr({}, 'actions'), subduxes)) || {};
|
||||||
|
|
||||||
Object.keys(mutations).forEach(type => {
|
Object.keys(mutations).forEach(type => {
|
||||||
@ -31,6 +33,12 @@ function buildActions({mutations = {}, subduxes = {}}) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Object.keys(effects).forEach(type => {
|
||||||
|
if (!actions[type]) {
|
||||||
|
actions[type] = actionFor(type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,13 +52,13 @@ function buildMutations({mutations = {}, subduxes = {}}) {
|
|||||||
// without, as the root '*' is not the same as any sub-'*'
|
// without, as the root '*' is not the same as any sub-'*'
|
||||||
|
|
||||||
const actions = fp.uniq( Object.keys(mutations).concat(
|
const actions = fp.uniq( Object.keys(mutations).concat(
|
||||||
...Object.values( subduxes ).map( ({mutations}) => Object.keys(mutations) )
|
...Object.values( subduxes ).map( ({mutations = {}}) => Object.keys(mutations) )
|
||||||
) );
|
) );
|
||||||
|
|
||||||
let mergedMutations = {};
|
let mergedMutations = {};
|
||||||
|
|
||||||
let [ globby, nonGlobby ] = fp.partition(
|
let [ globby, nonGlobby ] = fp.partition(
|
||||||
([_,{mutations}]) => mutations['*'],
|
([_,{mutations={}}]) => mutations['*'],
|
||||||
Object.entries(subduxes)
|
Object.entries(subduxes)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -66,7 +74,7 @@ function buildMutations({mutations = {}, subduxes = {}}) {
|
|||||||
mergedMutations[action] = [ globbyMutation ]
|
mergedMutations[action] = [ globbyMutation ]
|
||||||
});
|
});
|
||||||
|
|
||||||
nonGlobby.forEach( ([slice, {mutations,reducer}]) => {
|
nonGlobby.forEach( ([slice, {mutations={},reducer={}}]) => {
|
||||||
Object.entries(mutations).forEach(([type,mutation]) => {
|
Object.entries(mutations).forEach(([type,mutation]) => {
|
||||||
const localized = (payload=null,action={}) => u.updateIn( slice, mutation(payload,action) );
|
const localized = (payload=null,action={}) => u.updateIn( slice, mutation(payload,action) );
|
||||||
|
|
||||||
@ -82,27 +90,6 @@ function buildMutations({mutations = {}, subduxes = {}}) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildMiddleware({effects={},subduxes={}},{actions}) {
|
|
||||||
return api => {
|
|
||||||
|
|
||||||
for ( let type in actions ) {
|
|
||||||
api.dispatch[type] = (...args) => api.dispatch( actions[type](...args) );
|
|
||||||
}
|
|
||||||
|
|
||||||
return original_next => {
|
|
||||||
return [
|
|
||||||
...fp.toPairs(effects).map(([type,effect])=> {
|
|
||||||
return api => next => action => {
|
|
||||||
if( action.type !== type ) return next(action);
|
|
||||||
|
|
||||||
return effect(api)(next)(action);
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
...fp.map( 'middleware', subduxes )
|
|
||||||
].filter(x=>x).reduceRight( (next,mw) => mw(api)(next), original_next )
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updux(config) {
|
function updux(config) {
|
||||||
const dux = {};
|
const dux = {};
|
||||||
|
|
||||||
|
111
src/middleware.test.js
Normal file
111
src/middleware.test.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import updux from '.';
|
||||||
|
import u from 'updeep';
|
||||||
|
|
||||||
|
test( 'simple effect', () => {
|
||||||
|
|
||||||
|
const tracer = jest.fn();
|
||||||
|
|
||||||
|
const store = updux({
|
||||||
|
effects: {
|
||||||
|
foo: api => next => action => {
|
||||||
|
tracer();
|
||||||
|
next(action);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).createStore();
|
||||||
|
|
||||||
|
expect(tracer).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
store.dispatch({ type: 'bar' });
|
||||||
|
|
||||||
|
expect(tracer).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
store.dispatch.foo();
|
||||||
|
|
||||||
|
expect(tracer).toHaveBeenCalled();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
test( 'effect and sub-effect', () => {
|
||||||
|
|
||||||
|
const tracer = jest.fn();
|
||||||
|
|
||||||
|
const tracerEffect = signature => api => next => action => {
|
||||||
|
tracer(signature);
|
||||||
|
next(action);
|
||||||
|
};
|
||||||
|
|
||||||
|
const store = updux({
|
||||||
|
effects: {
|
||||||
|
foo: tracerEffect('root'),
|
||||||
|
},
|
||||||
|
subduxes: {
|
||||||
|
zzz: updux({effects: {
|
||||||
|
foo: tracerEffect('child'),
|
||||||
|
}})
|
||||||
|
},
|
||||||
|
}).createStore();
|
||||||
|
|
||||||
|
expect(tracer).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
store.dispatch({ type: 'bar' });
|
||||||
|
|
||||||
|
expect(tracer).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
store.dispatch.foo();
|
||||||
|
|
||||||
|
expect(tracer).toHaveBeenNthCalledWith(1,'root');
|
||||||
|
expect(tracer).toHaveBeenNthCalledWith(2,'child');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
test( '"*" effect', () => {
|
||||||
|
|
||||||
|
const tracer = jest.fn();
|
||||||
|
|
||||||
|
const store = updux({
|
||||||
|
effects: {
|
||||||
|
'*': api => next => action => {
|
||||||
|
tracer();
|
||||||
|
next(action);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).createStore();
|
||||||
|
|
||||||
|
expect(tracer).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
store.dispatch({ type: 'bar' });
|
||||||
|
|
||||||
|
expect(tracer).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test( 'async effect', async () => {
|
||||||
|
|
||||||
|
function timeout(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
const tracer = jest.fn();
|
||||||
|
|
||||||
|
const store = updux({
|
||||||
|
effects: {
|
||||||
|
foo: api => next => async action => {
|
||||||
|
next(action);
|
||||||
|
await timeout(1000);
|
||||||
|
tracer();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).createStore();
|
||||||
|
|
||||||
|
expect(tracer).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
store.dispatch.foo();
|
||||||
|
|
||||||
|
expect(tracer).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
await timeout(1000);
|
||||||
|
|
||||||
|
expect(tracer).toHaveBeenCalled();
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user