Merge branch 'middleware-maps'
This commit is contained in:
commit
54ad7f2512
@ -1,38 +1,38 @@
|
|||||||
import Updux from '.';
|
import Updux from ".";
|
||||||
import u from 'updeep';
|
import u from "updeep";
|
||||||
|
|
||||||
const noopEffect = () => () => () => {};
|
const noopEffect = () => () => () => {};
|
||||||
|
|
||||||
test('actions defined in effects and mutations, multi-level', () => {
|
test.only("actions defined in effects and mutations, multi-level", () => {
|
||||||
const {actions} = new Updux({
|
const { actions } = new Updux({
|
||||||
effects: {
|
effects: {
|
||||||
foo: noopEffect,
|
foo: noopEffect
|
||||||
},
|
},
|
||||||
mutations: {bar: () => () => null},
|
mutations: { bar: () => () => null },
|
||||||
subduxes: {
|
subduxes: {
|
||||||
mysub: {
|
mysub: {
|
||||||
effects: {baz: noopEffect },
|
effects: { baz: noopEffect },
|
||||||
mutations: {quux: () => () => null},
|
mutations: { quux: () => () => null },
|
||||||
actions: {
|
actions: {
|
||||||
foo: (limit:number) => ({limit}),
|
foo: (limit: number) => ({ limit })
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
myothersub: {
|
myothersub: {
|
||||||
effects: {
|
effects: {
|
||||||
foo: noopEffect,
|
foo: noopEffect
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const types = Object.keys(actions);
|
const types = Object.keys(actions);
|
||||||
types.sort();
|
types.sort();
|
||||||
|
|
||||||
expect(types).toEqual(['bar', 'baz', 'foo', 'quux']);
|
expect(types).toEqual(["bar", "baz", "foo", "quux"]);
|
||||||
|
|
||||||
expect(actions.bar()).toEqual({type: 'bar'});
|
expect(actions.bar()).toEqual({ type: "bar" });
|
||||||
expect(actions.bar('xxx')).toEqual({type: 'bar', payload: 'xxx'});
|
expect(actions.bar("xxx")).toEqual({ type: "bar", payload: "xxx" });
|
||||||
expect(actions.bar(undefined, 'yyy')).toEqual({type: 'bar', meta: 'yyy'});
|
expect(actions.bar(undefined, "yyy")).toEqual({ type: "bar", meta: "yyy" });
|
||||||
|
|
||||||
expect(actions.foo(12)).toEqual({type: 'foo', payload: {limit: 12}});
|
expect(actions.foo(12)).toEqual({ type: "foo", payload: { limit: 12 } });
|
||||||
});
|
});
|
||||||
|
@ -44,32 +44,14 @@ export function actionFor(type: string): ActionCreator {
|
|||||||
|
|
||||||
type ActionPair = [string, ActionCreator];
|
type ActionPair = [string, ActionCreator];
|
||||||
|
|
||||||
function buildActions(
|
function buildActions(actions: ActionPair[] = []): Dictionary<ActionCreator> {
|
||||||
generators: Dictionary<ActionPayloadGenerator> = {},
|
|
||||||
actionNames: string[] = [],
|
|
||||||
subActions: ActionPair[] = []
|
|
||||||
): Dictionary<ActionCreator> {
|
|
||||||
// priority => generics => generic subs => craft subs => creators
|
// priority => generics => generic subs => craft subs => creators
|
||||||
|
|
||||||
const [crafted, generic] = fp.partition(([type, f]) => !f._genericAction)(
|
const [crafted, generic] = fp.partition(([type, f]) => !f._genericAction)(
|
||||||
subActions
|
fp.compact(actions)
|
||||||
);
|
);
|
||||||
|
|
||||||
const actions: any = [
|
return fp.fromPairs([...generic, ...crafted]);
|
||||||
...actionNames.map(type => [type, actionFor(type)]),
|
|
||||||
...generic,
|
|
||||||
...crafted,
|
|
||||||
...Object.entries(
|
|
||||||
generators
|
|
||||||
).map(([type, payload]: [string, Function]): any => [
|
|
||||||
type,
|
|
||||||
(payload as any).type
|
|
||||||
? payload
|
|
||||||
: (...args: any) => ({ type, payload: payload(...args) })
|
|
||||||
])
|
|
||||||
];
|
|
||||||
|
|
||||||
return fp.fromPairs(actions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default buildActions;
|
export default buildActions;
|
||||||
|
@ -1,54 +1,57 @@
|
|||||||
import fp from 'lodash/fp';
|
import fp from "lodash/fp";
|
||||||
|
|
||||||
import { Middleware, MiddlewareAPI, Dispatch } from 'redux';
|
import { Middleware, MiddlewareAPI, Dispatch } from "redux";
|
||||||
import { Dictionary, ActionCreator, Action, UpduxDispatch, UpduxMiddleware, UpduxMiddlewareAPI } from '../types';
|
import {
|
||||||
|
Dictionary,
|
||||||
|
ActionCreator,
|
||||||
|
Action,
|
||||||
|
UpduxDispatch,
|
||||||
|
UpduxMiddleware,
|
||||||
|
UpduxMiddlewareAPI,
|
||||||
|
EffectEntry
|
||||||
|
} from "../types";
|
||||||
|
|
||||||
const MiddlewareFor = (type: any, mw: Middleware ): Middleware => api => next => action => {
|
const MiddlewareFor = (
|
||||||
if (type !== '*' && action.type !== type) return next(action);
|
type: any,
|
||||||
|
mw: Middleware
|
||||||
|
): Middleware => api => next => action => {
|
||||||
|
if (!["*", "^", "$"].includes(type) && action.type !== type)
|
||||||
|
return next(action);
|
||||||
|
|
||||||
return mw(api)(next)(action);
|
return mw(api)(next)(action);
|
||||||
};
|
};
|
||||||
|
|
||||||
type Next = (action: Action) => any;
|
type Next = (action: Action) => any;
|
||||||
|
|
||||||
function sliceMw( slice: string, mw: Middleware ): Middleware {
|
function sliceMw(slice: string, mw: Middleware): Middleware {
|
||||||
return (api) => {
|
return api => {
|
||||||
const getSliceState = () => fp.get(slice, api.getState() );
|
const getSliceState =
|
||||||
|
slice.length > 0 ? () => fp.get(slice, api.getState()) : api.getState;
|
||||||
const getRootState = (api as any).getRootState || api.getState;
|
const getRootState = (api as any).getRootState || api.getState;
|
||||||
return mw({...api, getState: getSliceState, getRootState} as any )
|
return mw({ ...api, getState: getSliceState, getRootState } as any);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildMiddleware<S=any>(
|
function buildMiddleware<S = any>(
|
||||||
effects : Dictionary<UpduxMiddleware<S>>= {},
|
middlewareEntries: any[] = [],
|
||||||
actions : Dictionary<ActionCreator>= {},
|
actions: Dictionary<ActionCreator> = {}
|
||||||
subduxes :any = {},
|
): UpduxMiddleware<S> {
|
||||||
): UpduxMiddleware<S>
|
let mws = middlewareEntries
|
||||||
{
|
.map(([slice, actionType, mw, isGen]: any) =>
|
||||||
|
isGen ? [slice, actionType, mw()] : [slice, actionType, mw]
|
||||||
const subMiddlewares = fp.flow(
|
)
|
||||||
fp.mapValues( fp.get('middleware') ),
|
.map(([slice, actionType, mw]) =>
|
||||||
fp.toPairs,
|
MiddlewareFor(actionType, sliceMw(slice, mw))
|
||||||
fp.filter(x=>x[1]),
|
);
|
||||||
fp.map( ([ slice, mw ]: [ string, Middleware]) => sliceMw(slice,mw) )
|
|
||||||
)( subduxes );
|
|
||||||
|
|
||||||
return (api: UpduxMiddlewareAPI<S>) => {
|
return (api: UpduxMiddlewareAPI<S>) => {
|
||||||
|
|
||||||
for (let type in actions) {
|
for (let type in actions) {
|
||||||
const ac = actions[type];
|
const ac = actions[type];
|
||||||
api.dispatch[type] = (...args:any[]) => api.dispatch(ac(...args));
|
api.dispatch[type] = (...args: any[]) => api.dispatch(ac(...args));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (original_next: Next)=> {
|
return (original_next: Next) => {
|
||||||
return [
|
return mws.reduceRight((next, mw) => mw(api)(next), original_next);
|
||||||
...fp.toPairs(effects).map(([type, effect]) =>
|
|
||||||
MiddlewareFor(type,effect as Middleware)
|
|
||||||
),
|
|
||||||
...subMiddlewares
|
|
||||||
]
|
|
||||||
.filter(x => x)
|
|
||||||
.reduceRight((next, mw) => mw(api)(next), original_next);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Updux from '.';
|
import Updux, { actionCreator } from ".";
|
||||||
import u from 'updeep';
|
import u from "updeep";
|
||||||
|
|
||||||
test('simple effect', () => {
|
test("simple effect", () => {
|
||||||
const tracer = jest.fn();
|
const tracer = jest.fn();
|
||||||
|
|
||||||
const store = new Updux({
|
const store = new Updux({
|
||||||
@ -9,13 +9,13 @@ test('simple effect', () => {
|
|||||||
foo: (api: any) => (next: any) => (action: any) => {
|
foo: (api: any) => (next: any) => (action: any) => {
|
||||||
tracer();
|
tracer();
|
||||||
next(action);
|
next(action);
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}).createStore();
|
}).createStore();
|
||||||
|
|
||||||
expect(tracer).not.toHaveBeenCalled();
|
expect(tracer).not.toHaveBeenCalled();
|
||||||
|
|
||||||
store.dispatch({type: 'bar'});
|
store.dispatch({ type: "bar" });
|
||||||
|
|
||||||
expect(tracer).not.toHaveBeenCalled();
|
expect(tracer).not.toHaveBeenCalled();
|
||||||
|
|
||||||
@ -24,11 +24,11 @@ test('simple effect', () => {
|
|||||||
expect(tracer).toHaveBeenCalled();
|
expect(tracer).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('effect and sub-effect', () => {
|
test("effect and sub-effect", () => {
|
||||||
const tracer = jest.fn();
|
const tracer = jest.fn();
|
||||||
|
|
||||||
const tracerEffect = (signature: string) => (api: any) => (next: any) => (
|
const tracerEffect = (signature: string) => (api: any) => (next: any) => (
|
||||||
action: any,
|
action: any
|
||||||
) => {
|
) => {
|
||||||
tracer(signature);
|
tracer(signature);
|
||||||
next(action);
|
next(action);
|
||||||
@ -36,27 +36,27 @@ test('effect and sub-effect', () => {
|
|||||||
|
|
||||||
const store = new Updux({
|
const store = new Updux({
|
||||||
effects: {
|
effects: {
|
||||||
foo: tracerEffect('root'),
|
foo: tracerEffect("root")
|
||||||
},
|
},
|
||||||
subduxes: {
|
subduxes: {
|
||||||
zzz: {
|
zzz: {
|
||||||
effects: {
|
effects: {
|
||||||
foo: tracerEffect('child'),
|
foo: tracerEffect("child")
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}).createStore();
|
}).createStore();
|
||||||
|
|
||||||
expect(tracer).not.toHaveBeenCalled();
|
expect(tracer).not.toHaveBeenCalled();
|
||||||
|
|
||||||
store.dispatch({type: 'bar'});
|
store.dispatch({ type: "bar" });
|
||||||
|
|
||||||
expect(tracer).not.toHaveBeenCalled();
|
expect(tracer).not.toHaveBeenCalled();
|
||||||
|
|
||||||
store.dispatch.foo();
|
store.dispatch.foo();
|
||||||
|
|
||||||
expect(tracer).toHaveBeenNthCalledWith(1, 'root');
|
expect(tracer).toHaveBeenNthCalledWith(1, "root");
|
||||||
expect(tracer).toHaveBeenNthCalledWith(2, 'child');
|
expect(tracer).toHaveBeenNthCalledWith(2, "child");
|
||||||
});
|
});
|
||||||
|
|
||||||
test('"*" effect', () => {
|
test('"*" effect', () => {
|
||||||
@ -64,21 +64,21 @@ test('"*" effect', () => {
|
|||||||
|
|
||||||
const store = new Updux({
|
const store = new Updux({
|
||||||
effects: {
|
effects: {
|
||||||
'*': api => next => action => {
|
"*": api => next => action => {
|
||||||
tracer();
|
tracer();
|
||||||
next(action);
|
next(action);
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}).createStore();
|
}).createStore();
|
||||||
|
|
||||||
expect(tracer).not.toHaveBeenCalled();
|
expect(tracer).not.toHaveBeenCalled();
|
||||||
|
|
||||||
store.dispatch({type: 'bar'});
|
store.dispatch({ type: "bar" });
|
||||||
|
|
||||||
expect(tracer).toHaveBeenCalled();
|
expect(tracer).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('async effect', async () => {
|
test("async effect", async () => {
|
||||||
function timeout(ms: number) {
|
function timeout(ms: number) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
@ -91,8 +91,8 @@ test('async effect', async () => {
|
|||||||
next(action);
|
next(action);
|
||||||
await timeout(1000);
|
await timeout(1000);
|
||||||
tracer();
|
tracer();
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}).createStore();
|
}).createStore();
|
||||||
|
|
||||||
expect(tracer).not.toHaveBeenCalled();
|
expect(tracer).not.toHaveBeenCalled();
|
||||||
@ -106,37 +106,123 @@ test('async effect', async () => {
|
|||||||
expect(tracer).toHaveBeenCalled();
|
expect(tracer).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('getState is local', () => {
|
test("getState is local", () => {
|
||||||
let childState;
|
let childState;
|
||||||
let rootState;
|
let rootState;
|
||||||
let rootFromChild;
|
let rootFromChild;
|
||||||
|
|
||||||
const child = new Updux({
|
const child = new Updux({
|
||||||
initial: {alpha: 12},
|
initial: { alpha: 12 },
|
||||||
effects: {
|
effects: {
|
||||||
doIt: ({getState,getRootState}) => next => action => {
|
doIt: ({ getState, getRootState }) => next => action => {
|
||||||
childState = getState();
|
childState = getState();
|
||||||
rootFromChild = getRootState();
|
rootFromChild = getRootState();
|
||||||
next(action);
|
next(action);
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const root = new Updux({
|
const root = new Updux({
|
||||||
initial: {beta: 24},
|
initial: { beta: 24 },
|
||||||
subduxes: {child},
|
subduxes: { child },
|
||||||
effects: {
|
effects: {
|
||||||
doIt: ({getState}) => next => action => {
|
doIt: ({ getState }) => next => action => {
|
||||||
rootState = getState();
|
rootState = getState();
|
||||||
next(action);
|
next(action);
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const store = root.createStore();
|
const store = root.createStore();
|
||||||
store.dispatch.doIt();
|
store.dispatch.doIt();
|
||||||
|
|
||||||
expect(rootState).toEqual({beta: 24, child: {alpha: 12}});
|
expect(rootState).toEqual({ beta: 24, child: { alpha: 12 } });
|
||||||
expect(rootFromChild).toEqual({beta: 24, child: {alpha: 12}});
|
expect(rootFromChild).toEqual({ beta: 24, child: { alpha: 12 } });
|
||||||
expect(childState).toEqual({alpha: 12});
|
expect(childState).toEqual({ alpha: 12 });
|
||||||
|
});
|
||||||
|
|
||||||
|
test("middleware as map", () => {
|
||||||
|
let childState;
|
||||||
|
let rootState;
|
||||||
|
let rootFromChild;
|
||||||
|
|
||||||
|
const doIt = actionCreator("doIt");
|
||||||
|
|
||||||
|
const child = new Updux({
|
||||||
|
initial: "",
|
||||||
|
effects: [
|
||||||
|
[
|
||||||
|
doIt,
|
||||||
|
() => next => action => {
|
||||||
|
next(u({ payload: (p: string) => p + "Child" }, action) as any);
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const root = new Updux({
|
||||||
|
initial: { message: "" },
|
||||||
|
subduxes: { child },
|
||||||
|
effects: [
|
||||||
|
[
|
||||||
|
"^",
|
||||||
|
() => next => action => {
|
||||||
|
next(u({ payload: (p: string) => p + "Pre" }, action) as any);
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
doIt,
|
||||||
|
() => next => action => {
|
||||||
|
next(u({ payload: (p: string) => p + "Root" }, action) as any);
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"*",
|
||||||
|
() => next => action => {
|
||||||
|
next(u({ payload: (p: string) => p + "After" }, action) as any);
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"$",
|
||||||
|
() => next => action => {
|
||||||
|
next(u({ payload: (p: string) => p + "End" }, action) as any);
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
mutations: [[doIt, (message: any) => () => ({ message })]]
|
||||||
|
});
|
||||||
|
|
||||||
|
const store = root.createStore();
|
||||||
|
store.dispatch.doIt("");
|
||||||
|
|
||||||
|
expect(store.getState()).toEqual({ message: "PreRootAfterChildEnd" });
|
||||||
|
});
|
||||||
|
|
||||||
|
test("generator", () => {
|
||||||
|
const updux = new Updux({
|
||||||
|
initial: 0,
|
||||||
|
mutations: [["doIt", payload => () => payload]],
|
||||||
|
effects: [
|
||||||
|
[
|
||||||
|
"doIt",
|
||||||
|
() => {
|
||||||
|
let i = 0;
|
||||||
|
return () => (next: any) => (action: any) =>
|
||||||
|
next({ ...action, payload: ++i });
|
||||||
|
},
|
||||||
|
true
|
||||||
|
]
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const store1 = updux.createStore();
|
||||||
|
store1.dispatch.doIt();
|
||||||
|
expect(store1.getState()).toEqual(1);
|
||||||
|
store1.dispatch.doIt();
|
||||||
|
expect(store1.getState()).toEqual(2);
|
||||||
|
updux.actions;
|
||||||
|
|
||||||
|
const store2 = updux.createStore();
|
||||||
|
store2.dispatch.doIt();
|
||||||
|
expect(store2.getState()).toEqual(1);
|
||||||
});
|
});
|
||||||
|
@ -183,9 +183,15 @@ export type UpduxConfig<S = any> = {
|
|||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
effects?: Dictionary<UpduxMiddleware<S>>;
|
effects?: Dictionary<UpduxMiddleware<S>> | EffectEntry<S>[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EffectEntry<S> = [
|
||||||
|
ActionCreator | string,
|
||||||
|
UpduxMiddleware<S>,
|
||||||
|
boolean?
|
||||||
|
];
|
||||||
|
|
||||||
export type Upreducer<S = any> = (action: Action) => (state: S) => S;
|
export type Upreducer<S = any> = (action: Action) => (state: S) => S;
|
||||||
|
|
||||||
export interface UpduxMiddlewareAPI<S> {
|
export interface UpduxMiddlewareAPI<S> {
|
||||||
|
115
src/updux.ts
115
src/updux.ts
@ -1,8 +1,7 @@
|
|||||||
import fp from "lodash/fp";
|
import fp from "lodash/fp";
|
||||||
import u from "updeep";
|
import u from "updeep";
|
||||||
import { observable, computed, toJS } from "mobx";
|
|
||||||
|
|
||||||
import buildActions, { actionFor } from "./buildActions";
|
import buildActions, { actionFor, actionCreator } from "./buildActions";
|
||||||
import buildInitial from "./buildInitial";
|
import buildInitial from "./buildInitial";
|
||||||
import buildMutations from "./buildMutations";
|
import buildMutations from "./buildMutations";
|
||||||
|
|
||||||
@ -18,7 +17,8 @@ import {
|
|||||||
Upreducer,
|
Upreducer,
|
||||||
UpduxDispatch,
|
UpduxDispatch,
|
||||||
UpduxMiddleware,
|
UpduxMiddleware,
|
||||||
MutationEntry
|
MutationEntry,
|
||||||
|
EffectEntry
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
import { Middleware, Store } from "redux";
|
import { Middleware, Store } from "redux";
|
||||||
@ -99,11 +99,11 @@ export class Updux<S = any> {
|
|||||||
*/
|
*/
|
||||||
groomMutations: (mutation: Mutation<S>) => Mutation<S>;
|
groomMutations: (mutation: Mutation<S>) => Mutation<S>;
|
||||||
|
|
||||||
@observable private localEffects: Dictionary<UpduxMiddleware<S>>;
|
private localEffects: EffectEntry<S>[] = [];
|
||||||
|
|
||||||
@observable private localActions: Dictionary<ActionCreator>;
|
private localActions: Dictionary<ActionCreator> = {};
|
||||||
|
|
||||||
@observable private localMutations: Dictionary<
|
private localMutations: Dictionary<
|
||||||
Mutation<S> | [Mutation<S>, boolean | undefined]
|
Mutation<S> | [Mutation<S>, boolean | undefined]
|
||||||
> = {};
|
> = {};
|
||||||
|
|
||||||
@ -114,9 +114,18 @@ export class Updux<S = any> {
|
|||||||
fp.isPlainObject(value) ? new Updux(value) : value
|
fp.isPlainObject(value) ? new Updux(value) : value
|
||||||
)(fp.getOr({}, "subduxes", config)) as Dictionary<Updux>;
|
)(fp.getOr({}, "subduxes", config)) as Dictionary<Updux>;
|
||||||
|
|
||||||
this.localActions = fp.getOr({}, "actions", config);
|
const actions = fp.getOr({}, "actions", config);
|
||||||
|
Object.entries(actions).forEach(([type, payload]: [string, any]): any =>
|
||||||
|
this.addAction(
|
||||||
|
(payload as any).type ? payload : actionCreator(type, payload as any)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
this.localEffects = fp.getOr({}, "effects", config);
|
let effects = fp.getOr([], "effects", config);
|
||||||
|
if (!Array.isArray(effects)) {
|
||||||
|
effects = Object.entries(effects);
|
||||||
|
}
|
||||||
|
effects.forEach(effect => this.addEffect(...effect));
|
||||||
|
|
||||||
this.initial = buildInitial<any>(
|
this.initial = buildInitial<any>(
|
||||||
config.initial,
|
config.initial,
|
||||||
@ -132,15 +141,15 @@ export class Updux<S = any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A middleware aggregating all the effects defined in the
|
* Array of middlewares aggregating all the effects defined in the
|
||||||
* updux and its subduxes. Effects of the updux itself are
|
* updux and its subduxes. Effects of the updux itself are
|
||||||
* done before the subduxes effects.
|
* done before the subduxes effects.
|
||||||
* Note that `getState` will always return the state of the
|
* Note that `getState` will always return the state of the
|
||||||
* local updux. The function `getRootState` is provided
|
* local updux. The function `getRootState` is provided
|
||||||
* alongside `getState` to get the root state.
|
* alongside `getState` to get the root state.
|
||||||
*/
|
*/
|
||||||
@computed get middleware(): UpduxMiddleware<S> {
|
get middleware(): UpduxMiddleware<S> {
|
||||||
return buildMiddleware(this.localEffects, this.actions, this.subduxes);
|
return buildMiddleware(this._middlewareEntries, this.actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,19 +166,19 @@ export class Updux<S = any> {
|
|||||||
* actions generated from mutations/effects < non-custom subduxes actions <
|
* actions generated from mutations/effects < non-custom subduxes actions <
|
||||||
* custom subduxes actions < custom actions
|
* custom subduxes actions < custom actions
|
||||||
*/
|
*/
|
||||||
@computed get actions(): Dictionary<ActionCreator> {
|
get actions(): Dictionary<ActionCreator> {
|
||||||
return buildActions(
|
return buildActions([
|
||||||
this.localActions,
|
...(Object.entries(this.localActions) as any),
|
||||||
[...Object.keys(this.localMutations), ...Object.keys(this.localEffects)],
|
...(fp.flatten(
|
||||||
fp.flatten(
|
|
||||||
Object.values(this.subduxes).map(({ actions }: Updux) =>
|
Object.values(this.subduxes).map(({ actions }: Updux) =>
|
||||||
Object.entries(actions)
|
Object.entries(actions)
|
||||||
)
|
)
|
||||||
)
|
) as any),
|
||||||
);
|
,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get upreducer(): Upreducer<S> {
|
get upreducer(): Upreducer<S> {
|
||||||
return buildUpreducer(this.initial, this.mutations);
|
return buildUpreducer(this.initial, this.mutations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +186,7 @@ export class Updux<S = any> {
|
|||||||
* A Redux reducer generated using the computed initial state and
|
* A Redux reducer generated using the computed initial state and
|
||||||
* mutations.
|
* mutations.
|
||||||
*/
|
*/
|
||||||
@computed get reducer(): (state: S | undefined, action: Action) => S {
|
get reducer(): (state: S | undefined, action: Action) => S {
|
||||||
return (state, action) => this.upreducer(action)(state as S);
|
return (state, action) => this.upreducer(action)(state as S);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +195,7 @@ export class Updux<S = any> {
|
|||||||
* mutations in both the main updux and its subduxes, the subduxes
|
* mutations in both the main updux and its subduxes, the subduxes
|
||||||
* mutations will be performed first.
|
* mutations will be performed first.
|
||||||
*/
|
*/
|
||||||
@computed get mutations(): Dictionary<Mutation<S>> {
|
get mutations(): Dictionary<Mutation<S>> {
|
||||||
return buildMutations(this.localMutations, this.subduxes);
|
return buildMutations(this.localMutations, this.subduxes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,7 +216,7 @@ export class Updux<S = any> {
|
|||||||
* );
|
* );
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
@computed get subduxUpreducer() {
|
get subduxUpreducer() {
|
||||||
return buildUpreducer(this.initial, buildMutations({}, this.subduxes));
|
return buildUpreducer(this.initial, buildMutations({}, this.subduxes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,14 +245,14 @@ export class Updux<S = any> {
|
|||||||
* // still work
|
* // still work
|
||||||
* store.dispatch( actions.addTodo(...) );
|
* store.dispatch( actions.addTodo(...) );
|
||||||
*/
|
*/
|
||||||
@computed get createStore(): () => StoreWithDispatchActions<S> {
|
get createStore(): () => StoreWithDispatchActions<S> {
|
||||||
const actions = this.actions;
|
const actions = this.actions;
|
||||||
|
|
||||||
return buildCreateStore<S>(
|
return buildCreateStore<S>(
|
||||||
this.reducer,
|
this.reducer,
|
||||||
this.initial,
|
this.initial,
|
||||||
this.middleware as Middleware,
|
this.middleware as any,
|
||||||
this.actions
|
actions
|
||||||
) as () => StoreWithDispatchActions<S, typeof actions>;
|
) as () => StoreWithDispatchActions<S, typeof actions>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,13 +294,67 @@ export class Updux<S = any> {
|
|||||||
) {
|
) {
|
||||||
let c = fp.isFunction(creator) ? creator : actionFor(creator);
|
let c = fp.isFunction(creator) ? creator : actionFor(creator);
|
||||||
|
|
||||||
this.localActions[c.type] = c;
|
this.addAction(c);
|
||||||
|
|
||||||
this.localMutations[c.type] = [
|
this.localMutations[c.type] = [
|
||||||
this.groomMutations(mutation as any) as Mutation<S>,
|
this.groomMutations(mutation as any) as Mutation<S>,
|
||||||
isSink
|
isSink
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addEffect(
|
||||||
|
creator: ActionCreator | string,
|
||||||
|
middleware: UpduxMiddleware<S>,
|
||||||
|
isGenerator: boolean = false
|
||||||
|
) {
|
||||||
|
let c = fp.isFunction(creator) ? creator : actionFor(creator);
|
||||||
|
|
||||||
|
this.addAction(c);
|
||||||
|
this.localActions[c.type] = c;
|
||||||
|
this.localEffects.push([c.type, middleware, isGenerator]);
|
||||||
|
}
|
||||||
|
|
||||||
|
addAction(action: string | ActionCreator<any>) {
|
||||||
|
if (typeof action === "string") {
|
||||||
|
if (!this.localActions[action]) {
|
||||||
|
this.localActions[action] = actionFor(action);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.localActions[action.type] = action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get _middlewareEntries() {
|
||||||
|
const groupByOrder = (mws: any) =>
|
||||||
|
fp.groupBy(
|
||||||
|
([_, actionType]: any) =>
|
||||||
|
["^", "$"].includes(actionType) ? actionType : "middle",
|
||||||
|
mws
|
||||||
|
);
|
||||||
|
|
||||||
|
let subs = fp.flow([
|
||||||
|
fp.mapValues("_middlewareEntries"),
|
||||||
|
fp.toPairs,
|
||||||
|
fp.map(([slice, entries]) =>
|
||||||
|
entries.map(([ps, ...args]: any) => [[slice, ...ps], ...args])
|
||||||
|
),
|
||||||
|
fp.flatten,
|
||||||
|
groupByOrder
|
||||||
|
])(this.subduxes);
|
||||||
|
|
||||||
|
let local = groupByOrder(this.localEffects.map(x => [[], ...x]));
|
||||||
|
|
||||||
|
return fp.flatten(
|
||||||
|
[
|
||||||
|
local["^"],
|
||||||
|
subs["^"],
|
||||||
|
local.middle,
|
||||||
|
subs.middle,
|
||||||
|
subs["$"],
|
||||||
|
local["$"]
|
||||||
|
].filter(x => x)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Updux;
|
export default Updux;
|
||||||
|
Loading…
Reference in New Issue
Block a user