Merge branch 'upreducerWrapper' into return-to-ts

This commit is contained in:
Yanick Champoux 2021-10-18 11:19:59 -04:00
commit 541790425a
5 changed files with 66 additions and 9 deletions

View File

@ -11,7 +11,14 @@ import { action } from './actions';
import { buildUpreducer } from './buildUpreducer'; import { buildUpreducer } from './buildUpreducer';
import { buildMiddleware, augmentMiddlewareApi } from './buildMiddleware'; 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. * Configuration object typically passed to the constructor of the class Updux.
@ -70,6 +77,28 @@ export interface UpduxConfig<
* reaction for the mapped dux. * reaction for the mapped dux.
*/ */
mappedReaction?: Function | boolean; 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< export class Updux<
@ -90,6 +119,7 @@ export class Updux<
#reactions = []; #reactions = [];
#mappedSelectors = undefined; #mappedSelectors = undefined;
#mappedReaction = undefined; #mappedReaction = undefined;
#upreducerWrapper = undefined;
constructor(config: UpduxConfig<TState, TActions, TSelectors, TSubduxes>) { constructor(config: UpduxConfig<TState, TActions, TSelectors, TSubduxes>) {
this.#initial = config.initial ?? {}; this.#initial = config.initial ?? {};
@ -130,6 +160,8 @@ export class Updux<
this.#reactions = config.reactions ?? []; this.#reactions = config.reactions ?? [];
this.#mappedReaction = config.mappedReaction; this.#mappedReaction = config.mappedReaction;
this.#upreducerWrapper = config.upreducerWrapper;
} }
#memoInitial = moize(buildInitial); #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( return this.#memoUpreducer(
this.initial, this.initial,
this.#mutations, 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); return (state, action) => this.upreducer(action)(state);
} }
@ -340,7 +379,7 @@ export class Updux<
selectors: Record<string, Function>; selectors: Record<string, Function>;
actions: AggregateDuxActions<TActions, TSubduxes>; actions: AggregateDuxActions<TActions, TSubduxes>;
} = reduxCreateStore( } = reduxCreateStore(
this.reducer, this.reducer as any,
initial ?? this.initial, initial ?? this.initial,
enhancer enhancer
) as any; ) 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; type: T;
meta?: Record<string, unknown>; meta?: Record<string, unknown>;
} & { } & {

View File

@ -1,13 +1,18 @@
import u from 'updeep'; import u from 'updeep';
import { mapValues } from 'lodash'; import { mapValues } from 'lodash';
export function buildUpreducer(initial, mutations, subduxes = {}) { export function buildUpreducer(
initial,
mutations,
subduxes = {},
wrapper = undefined
) {
const subReducers = const subReducers =
Object.keys(subduxes).length > 0 Object.keys(subduxes).length > 0
? mapValues(subduxes, ({ upreducer }) => upreducer) ? mapValues(subduxes, ({ upreducer }) => upreducer)
: null; : null;
return (action) => (state) => { const upreducer = (action) => (state) => {
if (!action?.type) if (!action?.type)
throw new Error('upreducer called with a bad action'); 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 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 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> = ( export type UnionToIntersection<U> = (
U extends any ? (k: U) => void : never U extends any ? (k: U) => void : never
) extends (k: infer I) => void ) 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 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 & export type AggregateDuxActions<TActions, TSubduxes> = TActions &
UnionToIntersection<ActionsOf<ItemsOf<TSubduxes>>>; UnionToIntersection<ActionsOf<ItemsOf<TSubduxes>>>;