initial reducer

This commit is contained in:
Yanick Champoux 2023-03-08 11:47:21 -05:00
parent 93533a739f
commit 4c28a9ad05
4 changed files with 115 additions and 22 deletions

View File

@ -41,11 +41,13 @@ type ResolveActions<
: never; : never;
}; };
type Mutation<A extends ActionCreator<any> = ActionCreator<any>, S = any> = ( export type Mutation<
state: S, A extends ActionCreator<any> = ActionCreator<any>,
S = any,
> = (
payload: ReturnType<A>['payload'], payload: ReturnType<A>['payload'],
action: ReturnType<A>, action: ReturnType<A>,
) => S | void; ) => (state: S) => S | void;
export default class Updux< export default class Updux<
T_LocalState = Record<any, any>, T_LocalState = Record<any, any>,

View File

@ -1,19 +0,0 @@
import { test, expect } from 'vitest';
import { Updux } from './Updux.js';
test('basic reducer', () => {
const dux = new Updux({ initial: { a: 3 } });
expect(dux.reducer).toBeTypeOf('function');
expect(dux.reducer({ a: 1 }, { type: 'foo' })).toMatchObject({ a: 1 }); // noop
});
test('basic upreducer', () => {
const dux = new Updux({ initial: { a: 3 } });
expect(dux.upreducer).toBeTypeOf('function');
expect(dux.upreducer({ type: 'foo' })({ a: 1 })).toMatchObject({ a: 1 }); // noop
});

31
src/reducer.test.ts Normal file
View File

@ -0,0 +1,31 @@
import { test, expect } from 'vitest';
import { buildReducer } from './reducer.js';
import Updux from './Updux.js';
test('buildReducer, initial state', () => {
const reducer = buildReducer({ a: 1 });
expect(reducer(undefined, { type: 'foo' })).toEqual({ a: 1 });
});
test('buildReducer, mutation', () => {
const reducer = buildReducer(1, [
{
matcher: ({ type }) => type === 'inc',
mutation: () => (state) => state + 1,
terminal: false,
},
]);
expect(reducer(undefined, { type: 'foo' })).toEqual(1);
expect(reducer(undefined, { type: 'inc' })).toEqual(2);
});
test.todo('basic reducer', () => {
const dux = new Updux({ initial: { a: 3 } });
expect(dux.reducer).toBeTypeOf('function');
expect(dux.reducer({ a: 1 }, { type: 'foo' })).toMatchObject({ a: 1 }); // noop
});

79
src/reducer.ts Normal file
View File

@ -0,0 +1,79 @@
import { Action, ActionCreator, createAction } from '@reduxjs/toolkit';
import { BaseActionCreator } from '@reduxjs/toolkit/dist/createAction.js';
import * as R from 'remeda';
import { Dux } from './types.js';
import { Mutation } from './Updux.js';
type MutationCase = {
matcher: (action: Action) => boolean;
mutation: Mutation;
terminal: boolean;
};
export function buildReducer(
initialState: any,
mutations: MutationCase[] = [],
subduxes: Record<string, Dux> = {},
) {
// const subReducers =
// ? R.mapValues(subduxes, R.prop('reducer'));
// TODO matcherMutation
// TODO defaultMutation
const reducer = (state = initialState, action: Action) => {
if (!action?.type)
throw new Error('upreducer called with a bad action');
let terminal = false;
let didSomething = false;
const foo = createAction('foo');
const localMutation = mutations.find(({ matcher }) => matcher(action));
if (localMutation) {
didSomething = true;
if (localMutation.terminal) terminal = true;
// TODO wrap mutations in immer
state = localMutation.mutation(
(action as any).payload,
action,
)(state);
}
// TODO defaultMutation
return state;
};
return reducer;
/*
if (subReducers) {
if (subduxes['*']) {
newState = u.updateIn(
'*',
subduxes['*'].upreducer(action),
newState,
);
} else {
const update = mapValues(subReducers, (upReducer) =>
upReducer(action),
);
newState = u(update, newState);
}
}
const a = mutations[action.type] || mutations['+'];
if (!a) return newState;
return a(action.payload, action)(newState);
};
return wrapper ? wrapper(upreducer) : upreducer;
*/
}