selectors and store
This commit is contained in:
parent
347d90267e
commit
f322691906
18
src/Updux.ts
18
src/Updux.ts
@ -19,11 +19,18 @@ import { AggregateActions, AggregateSelectors, Dux } from './types.js';
|
|||||||
import { buildActions } from './buildActions.js';
|
import { buildActions } from './buildActions.js';
|
||||||
import { buildInitial, AggregateState } from './initial.js';
|
import { buildInitial, AggregateState } from './initial.js';
|
||||||
import { buildReducer, MutationCase } from './reducer.js';
|
import { buildReducer, MutationCase } from './reducer.js';
|
||||||
import { buildEffectsMiddleware, EffectMiddleware } from './effects.js';
|
import { augmentGetState, buildEffectsMiddleware, EffectMiddleware } from './effects.js';
|
||||||
import { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore.js';
|
import { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore.js';
|
||||||
|
|
||||||
type MyActionCreator = { type: string } & ((...args: any) => any);
|
type MyActionCreator = { type: string } & ((...args: any) => any);
|
||||||
|
|
||||||
|
type XSel<R> = R extends Function ? R : () => R;
|
||||||
|
type CurriedSelector<S> = S extends (...args: any) => infer R ? XSel<R> : never;
|
||||||
|
|
||||||
|
type CurriedSelectors<S> = {
|
||||||
|
[key in keyof S]: CurriedSelector<S[key]>
|
||||||
|
}
|
||||||
|
|
||||||
type ResolveAction<
|
type ResolveAction<
|
||||||
ActionType extends string,
|
ActionType extends string,
|
||||||
ActionArg extends any,
|
ActionArg extends any,
|
||||||
@ -176,6 +183,8 @@ export default class Updux<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
store.getState = augmentGetState(store.getState, this.selectors);
|
||||||
|
|
||||||
return store as ToolkitStore<
|
return store as ToolkitStore<
|
||||||
AggregateState<T_LocalState, T_Subduxes>
|
AggregateState<T_LocalState, T_Subduxes>
|
||||||
> & {
|
> & {
|
||||||
@ -183,6 +192,12 @@ export default class Updux<
|
|||||||
ResolveActions<T_LocalActions>,
|
ResolveActions<T_LocalActions>,
|
||||||
T_Subduxes
|
T_Subduxes
|
||||||
>;
|
>;
|
||||||
|
} & {
|
||||||
|
getState: CurriedSelectors<AggregateSelectors<
|
||||||
|
T_LocalSelectors,
|
||||||
|
T_Subduxes,
|
||||||
|
AggregateState<T_LocalState, T_Subduxes>
|
||||||
|
>>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,3 +272,4 @@ export default class Updux<
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,31 +1,42 @@
|
|||||||
import { mapValues, map, get } from 'lodash-es';
|
import { mapValues, map, get } from 'lodash-es';
|
||||||
const middlewareFor = (type, middleware) => (api) => (next) => (action) => {
|
const middlewareFor = (type, middleware) => (api) => (next) => (action) => {
|
||||||
if (type !== '*' && action.type !== type)
|
if (type !== '*' && action.type !== type) return next(action);
|
||||||
return next(action);
|
|
||||||
return middleware(api)(next)(action);
|
return middleware(api)(next)(action);
|
||||||
};
|
};
|
||||||
const sliceMw = (slice, mw) => (api) => {
|
const sliceMw = (slice, mw) => (api) => {
|
||||||
const getSliceState = () => get(api.getState(), slice);
|
const getSliceState = () => get(api.getState(), slice);
|
||||||
return mw(Object.assign(Object.assign({}, api), { getState: getSliceState }));
|
return mw(
|
||||||
|
Object.assign(Object.assign({}, api), { getState: getSliceState }),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
export function augmentMiddlewareApi(api, actions, selectors) {
|
|
||||||
const getState = () => api.getState();
|
export const augmentGetState = (getState, selectors) =>
|
||||||
const dispatch = (action) => api.dispatch(action);
|
Object.assign(
|
||||||
Object.assign(getState, mapValues(selectors, (selector) => {
|
getState,
|
||||||
|
mapValues(selectors, (selector) => {
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
let result = selector(api.getState());
|
let result = selector(api.getState());
|
||||||
if (typeof result === 'function')
|
if (typeof result === 'function') return result(...args);
|
||||||
return result(...args);
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
}));
|
}),
|
||||||
Object.assign(dispatch, mapValues(actions, (action) => {
|
);
|
||||||
|
|
||||||
|
export function augmentMiddlewareApi(api, actions, selectors) {
|
||||||
|
const getState = augmentGetState(() => api.getState(), selectors);
|
||||||
|
const dispatch = (action) => api.dispatch(action);
|
||||||
|
Object.assign(
|
||||||
|
dispatch,
|
||||||
|
mapValues(actions, (action) => {
|
||||||
return (...args) => api.dispatch(action(...args));
|
return (...args) => api.dispatch(action(...args));
|
||||||
}));
|
}),
|
||||||
return Object.assign(Object.assign({}, api), { getState,
|
);
|
||||||
|
return Object.assign(Object.assign({}, api), {
|
||||||
|
getState,
|
||||||
dispatch,
|
dispatch,
|
||||||
actions,
|
actions,
|
||||||
selectors });
|
selectors,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
export const effectToMiddleware = (effect, actions, selectors) => {
|
export const effectToMiddleware = (effect, actions, selectors) => {
|
||||||
let mw = effect;
|
let mw = effect;
|
||||||
@ -37,12 +48,23 @@ export const effectToMiddleware = (effect, actions, selectors) => {
|
|||||||
}
|
}
|
||||||
return (api) => mw(augmentMiddlewareApi(api, actions, selectors));
|
return (api) => mw(augmentMiddlewareApi(api, actions, selectors));
|
||||||
};
|
};
|
||||||
const composeMw = (mws) => (api) => (original_next) => mws.reduceRight((next, mw) => mw(api)(next), original_next);
|
const composeMw = (mws) => (api) => (original_next) =>
|
||||||
export function buildMiddleware(effects = [], actions = {}, selectors = {}, sub = {}, wrapper = undefined, dux = undefined) {
|
mws.reduceRight((next, mw) => mw(api)(next), original_next);
|
||||||
let inner = map(sub, ({ middleware }, slice) => slice !== '*' && middleware ? sliceMw(slice, middleware) : undefined).filter((x) => x);
|
export function buildMiddleware(
|
||||||
const local = effects.map((effect) => effectToMiddleware(effect, actions, selectors));
|
effects = [],
|
||||||
|
actions = {},
|
||||||
|
selectors = {},
|
||||||
|
sub = {},
|
||||||
|
wrapper = undefined,
|
||||||
|
dux = undefined,
|
||||||
|
) {
|
||||||
|
let inner = map(sub, ({ middleware }, slice) =>
|
||||||
|
slice !== '*' && middleware ? sliceMw(slice, middleware) : undefined,
|
||||||
|
).filter((x) => x);
|
||||||
|
const local = effects.map((effect) =>
|
||||||
|
effectToMiddleware(effect, actions, selectors),
|
||||||
|
);
|
||||||
let mws = [...local, ...inner];
|
let mws = [...local, ...inner];
|
||||||
if (wrapper)
|
if (wrapper) mws = wrapper(mws, dux);
|
||||||
mws = wrapper(mws, dux);
|
|
||||||
return composeMw(mws);
|
return composeMw(mws);
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,17 @@ export interface EffectMiddleware<S = any, D extends Dispatch = Dispatch> {
|
|||||||
const composeMw = (mws) => (api) => (originalNext) =>
|
const composeMw = (mws) => (api) => (originalNext) =>
|
||||||
mws.reduceRight((next, mw) => mw(api)(next), originalNext);
|
mws.reduceRight((next, mw) => mw(api)(next), originalNext);
|
||||||
|
|
||||||
|
export const augmentGetState = (originalGetState, selectors) => {
|
||||||
|
const getState = () => originalGetState();
|
||||||
|
for (const s in selectors) {
|
||||||
|
getState[s] = (...args) => {
|
||||||
|
let result = selectors[s](originalGetState());
|
||||||
|
if (typeof result === 'function') return result(...args);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return getState;
|
||||||
|
};
|
||||||
export function buildEffectsMiddleware(
|
export function buildEffectsMiddleware(
|
||||||
effects = [],
|
effects = [],
|
||||||
actions = {},
|
actions = {},
|
||||||
|
@ -17,6 +17,10 @@ test('basic selectors', () => {
|
|||||||
initial: { y: 2 },
|
initial: { y: 2 },
|
||||||
selectors: {
|
selectors: {
|
||||||
getY: ({ y }: { y: number }) => y,
|
getY: ({ y }: { y: number }) => y,
|
||||||
|
getYPlus:
|
||||||
|
({ y }) =>
|
||||||
|
(incr: number) =>
|
||||||
|
(y + incr) as number,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -29,4 +33,10 @@ test('basic selectors', () => {
|
|||||||
|
|
||||||
expect(foo.selectors.getY(sample)).toBe(3);
|
expect(foo.selectors.getY(sample)).toBe(3);
|
||||||
expect(foo.selectors.getX(sample)).toBe(4);
|
expect(foo.selectors.getX(sample)).toBe(4);
|
||||||
|
|
||||||
|
expect(foo.selectors.getYPlus(sample)(3)).toBe(6);
|
||||||
|
|
||||||
|
const store = foo.createStore();
|
||||||
|
expect(store.getState.getY()).toBe(2);
|
||||||
|
expect(store.getState.getYPlus(3)).toBe(5);
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user