defaultMutation

main
Yanick Champoux 2023-03-09 10:59:00 -05:00
parent 2e03a51e15
commit 76ccd0d14a
3 changed files with 44 additions and 3 deletions

View File

@ -15,7 +15,7 @@ import {
import { AggregateActions, Dux } from './types.js';
import { buildActions } from './buildActions.js';
import { buildInitial, AggregateState } from './initial.js';
import { buildReducer } from './reducer.js';
import { buildReducer, MutationCase } from './reducer.js';
type MyActionCreator = { type: string } & ((...args: any) => any);
@ -61,6 +61,7 @@ export default class Updux<
#localInitial: T_LocalState;
#localActions: T_LocalActions;
#localMutations: MutationCase[] = [];
#defaultMutation: Omit<MutationCase, 'matcher'>;
#subduxes: T_Subduxes;
#name: string;
@ -111,12 +112,17 @@ export default class Updux<
// TODO memoize this sucker
get reducer() {
return buildReducer(this.initial, this.#localMutations) as any as (
return buildReducer(
this.initial,
this.#localMutations,
this.#defaultMutation,
) as any as (
state: undefined | typeof this.initial,
action: Action,
) => typeof this.initial;
}
// TODO be smarter with the guard?
addMutation<A extends Action<any>>(
matcher: (action: A) => boolean,
mutation: Mutation<A, AggregateState<T_LocalState, T_Subduxes>>,
@ -142,4 +148,14 @@ export default class Updux<
mutation,
});
}
addDefaultMutation(
mutation: Mutation<
Action<any>,
AggregateState<T_LocalState, T_Subduxes>
>,
terminal = false,
) {
this.#defaultMutation = { mutation, terminal };
}
}

View File

@ -37,3 +37,19 @@ test('catch-all mutation', () => {
expect(dux.reducer(undefined, { type: 'foo' })).toEqual('got it');
});
test('default mutation', () => {
const dux = new Updux({
initial: '',
actions: {
foo: 0,
},
});
dux.addMutation(dux.actions.foo, () => () => 'got it');
dux.addDefaultMutation((_payload, action) => () => action.type);
expect(dux.reducer(undefined, { type: 'foo' })).toEqual('got it');
expect(dux.reducer(undefined, { type: 'bar' })).toEqual('bar');
});

View File

@ -13,6 +13,7 @@ export type MutationCase = {
export function buildReducer(
initialState: any,
mutations: MutationCase[] = [],
defaultMutation?: Omit<MutationCase, 'matcher'>,
subduxes: Record<string, Dux> = {},
) {
// const subReducers =
@ -32,12 +33,20 @@ export function buildReducer(
.filter(({ matcher }) => matcher(action))
.forEach(({ mutation, terminal: t }) => {
if (t) terminal = true;
didSomething = true;
//
// TODO wrap mutations in immer
state = mutation((action as any).payload, action)(state);
});
// TODO defaultMutation
if (!didSomething && defaultMutation) {
if (defaultMutation.terminal) terminal = true;
state = defaultMutation.mutation(
(action as any).payload,
action,
)(state);
}
return state;
};