add upreducerWrapper

typescript
Yanick Champoux 2021-10-18 10:54:28 -04:00
parent 8314ff94ca
commit f1b7677f0f
5 changed files with 66 additions and 9 deletions

View File

@ -11,7 +11,14 @@ import { action } from './actions';
import { buildUpreducer } from './buildUpreducer';
import { buildMiddleware, augmentMiddlewareApi } from './buildMiddleware';
import { AggregateDuxActions, AggregateDuxState, Dict } from './types';
import {
AggregateDuxActions,
AggregateDuxState,
Dict,
ItemsOf,
Reducer,
Upreducer,
} from './types';
/**
* Configuration object typically passed to the constructor of the class Updux.
@ -70,6 +77,28 @@ export interface UpduxConfig<
* reaction for the mapped dux.
*/
mappedReaction?: Function | boolean;
/**
* Wrapping function for the upreducer to provides full customization.
* @example
* // if an action has the 'dontDoIt' meta flag, don't do anything
* const dux = new Updux({
* ...,
* upreducerWrapper: (upreducer) => action => {
* if( action?.meta?.dontDoIt ) return state => state;
* return upreducer(action);
* }
* })
*/
upreducerWrapper?: (
upreducer: Upreducer<
AggregateDuxState<TState, TSubduxes>,
ItemsOf<AggregateDuxActions<TActions, TSubduxes>>
>
) => Upreducer<
AggregateDuxState<TState, TSubduxes>,
ItemsOf<AggregateDuxActions<TActions, TSubduxes>>
>;
}
export class Updux<
@ -90,6 +119,7 @@ export class Updux<
#reactions = [];
#mappedSelectors = undefined;
#mappedReaction = undefined;
#upreducerWrapper = undefined;
constructor(config: UpduxConfig<TState, TActions, TSelectors, TSubduxes>) {
this.#initial = config.initial ?? {};
@ -130,6 +160,8 @@ export class Updux<
this.#reactions = config.reactions ?? [];
this.#mappedReaction = config.mappedReaction;
this.#upreducerWrapper = config.upreducerWrapper;
}
#memoInitial = moize(buildInitial);
@ -171,15 +203,22 @@ export class Updux<
);
}
get upreducer() {
get upreducer(): Upreducer<
AggregateDuxState<TState, TSubduxes>,
ItemsOf<AggregateDuxActions<TActions, TSubduxes>>
> {
return this.#memoUpreducer(
this.initial,
this.#mutations,
this.#subduxes
this.#subduxes,
this.#upreducerWrapper
);
}
get reducer() {
get reducer(): Reducer<
AggregateDuxState<TState, TSubduxes>,
ItemsOf<AggregateDuxActions<TActions, TSubduxes>>
> {
return (state, action) => this.upreducer(action)(state);
}
@ -340,7 +379,7 @@ export class Updux<
selectors: Record<string, Function>;
actions: AggregateDuxActions<TActions, TSubduxes>;
} = reduxCreateStore(
this.reducer,
this.reducer as any,
initial ?? this.initial,
enhancer
) as any;

View File

@ -1,4 +1,4 @@
export type Action<T extends string = string, TPayload = unknown> = {
export type Action<T extends string = string, TPayload = any> = {
type: T;
meta?: Record<string, unknown>;
} & {

View File

@ -1,13 +1,18 @@
import u from 'updeep';
import { mapValues } from 'lodash';
export function buildUpreducer(initial, mutations, subduxes = {}) {
export function buildUpreducer(
initial,
mutations,
subduxes = {},
wrapper = undefined
) {
const subReducers =
Object.keys(subduxes).length > 0
? mapValues(subduxes, ({ upreducer }) => upreducer)
: null;
return (action) => (state) => {
const upreducer = (action) => (state) => {
if (!action?.type)
throw new Error('upreducer called with a bad action');
@ -35,4 +40,6 @@ export function buildUpreducer(initial, mutations, subduxes = {}) {
return a(action.payload, action)(newState);
};
return wrapper ? wrapper(upreducer) : upreducer;
}

View File

@ -1,5 +1,16 @@
import { Action } from './actions';
export type Dict<T> = Record<string, T>;
export type Upreducer<TState = any, TAction = Action> = (
action: Action
) => (state: TState) => TState;
export type Reducer<TState = any, TAction = Action> = (
state: TState | undefined,
action: Action
) => TState;
export type UnionToIntersection<U> = (
U extends any ? (k: U) => void : never
) extends (k: infer I) => void
@ -31,7 +42,7 @@ export type AggregateDuxState<TState, TSubduxes> = TState &
type DuxActionsSubduxes<C> = C extends object ? ActionsOf<C[keyof C]> : unknown;
type ItemsOf<C> = C extends object ? C[keyof C] : unknown;
export type ItemsOf<C> = C extends object ? C[keyof C] : unknown;
export type AggregateDuxActions<TActions, TSubduxes> = TActions &
UnionToIntersection<ActionsOf<ItemsOf<TSubduxes>>>;