updux/src/reducer.ts

64 lines
1.9 KiB
TypeScript
Raw Normal View History

2023-09-06 19:09:45 +00:00
import { Action } from '@reduxjs/toolkit';
2023-03-08 16:47:21 +00:00
import * as R from 'remeda';
2023-09-06 19:09:45 +00:00
import { DuxConfig } from './types.js';
2023-03-08 16:47:21 +00:00
import { Mutation } from './Updux.js';
2023-03-22 16:04:07 +00:00
import u from '@yanick/updeep-remeda';
2023-03-08 16:47:21 +00:00
2023-03-09 15:41:15 +00:00
export type MutationCase = {
2023-03-08 16:47:21 +00:00
matcher: (action: Action) => boolean;
mutation: Mutation;
terminal: boolean;
};
export function buildReducer(
2023-09-06 19:09:45 +00:00
initialStateState: unknown,
2023-03-08 16:47:21 +00:00
mutations: MutationCase[] = [],
2023-03-09 15:59:00 +00:00
defaultMutation?: Omit<MutationCase, 'matcher'>,
2023-09-06 19:09:45 +00:00
subduxes: Record<string, DuxConfig> = {},
2023-03-08 16:47:21 +00:00
) {
2023-03-09 20:13:13 +00:00
const subReducers = R.mapValues(subduxes, R.prop('reducer'));
2023-03-08 16:47:21 +00:00
// TODO matcherMutation
// TODO defaultMutation
2023-03-09 15:41:15 +00:00
//
2023-03-23 22:15:32 +00:00
const reducer = (state = initialStateState, action: Action) => {
2023-09-06 19:09:45 +00:00
if (!action?.type) throw new Error('reducer called with a bad action');
let active = mutations.filter(({ matcher }) => matcher(action));
if (active.length === 0 && defaultMutation)
active.push(defaultMutation as any);
if (
!active.some(R.prop<any, any>('terminal')) &&
Object.values(subReducers).length > 0
) {
active.push({
mutation: (payload, action) => (state) => {
return u(
state,
R.mapValues(
subReducers,
(reducer, slice) => (state) => {
return (reducer as any)(state, action);
},
),
);
},
} as any);
2023-03-09 20:13:13 +00:00
}
2023-09-06 19:09:45 +00:00
// frozen objects don't play well with immer
// if (Object.isFrozen(state)) {
// state = { ...(state as any) };
// }
return active.reduce(
(state, { mutation }) =>
mutation((action as any).payload, action)(state),
state,
);
2023-03-08 16:47:21 +00:00
};
return reducer;
}