Merge branch 'mutations-of-subduxes' into typescript

main
Yanick Champoux 2023-03-09 15:32:46 -05:00
commit 3e9049ce93
5 changed files with 38 additions and 96 deletions

View File

@ -116,6 +116,7 @@ export default class Updux<
this.initial,
this.#localMutations,
this.#defaultMutation,
this.#subduxes,
) as any as (
state: undefined | typeof this.initial,
action: Action,

View File

@ -1,45 +0,0 @@
import u from 'updeep';
import { mapValues } from 'lodash-es';
export function buildUpreducer(
initial,
mutations,
subduxes = {},
wrapper = undefined,
) {
const subReducers =
Object.keys(subduxes).length > 0
? mapValues(subduxes, ({ upreducer }) => upreducer)
: null;
const upreducer = (action) => (state) => {
if (!action?.type)
throw new Error('upreducer called with a bad action');
let newState = state ?? initial;
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;
}

View File

@ -1,48 +0,0 @@
import schema from 'json-schema-shorthand';
import u from 'updeep';
import { action } from './actions.js';
import { Updux, dux } from './Updux.js';
test('mutation of a subdux', async () => {
const bar = dux({
actions: {
baz: null,
},
});
bar.setMutation('baz', () => (state) => ({ ...state, x: 1 }));
const foo = dux({
subduxes: { bar },
});
const store = foo.createStore();
store.dispatch.baz();
expect(store.getState()).toMatchObject({ bar: { x: 1 } });
});
test('strings and generators', async () => {
const actionA = action('a');
const foo = dux({
actions: {
b: null,
a: actionA,
},
});
// as a string and defined
expect(() => foo.setMutation('a', () => {})).not.toThrow();
// as a generator and defined
expect(() => foo.setMutation(actionA, () => {})).not.toThrow();
// as a string, not defined
expect(() => foo.setMutation('c', () => {})).toThrow();
foo.setMutation(action('d'), () => {});
expect(foo.actions.d).toBeTypeOf('function');
});

View File

@ -1,6 +1,6 @@
import { test, expect } from 'vitest';
import Updux from './index.js';
import Updux, { createAction } from './index.js';
test('set a mutation', () => {
const dux = new Updux({
@ -53,3 +53,28 @@ test('default mutation', () => {
expect(dux.reducer(undefined, { type: 'foo' })).toEqual('got it');
expect(dux.reducer(undefined, { type: 'bar' })).toEqual('bar');
});
test('mutation of a subdux', () => {
const baz = createAction('baz');
const noop = createAction('noop');
const stopit = createAction('stopit');
const bar = new Updux({
initial: 0,
actions: {
baz,
stopit,
},
});
bar.addMutation(baz, () => () => 1);
bar.addMutation(stopit, () => () => 2);
const foo = new Updux({
subduxes: { bar },
});
foo.addMutation(stopit, () => (state) => state, true);
expect(foo.reducer(undefined, noop())).toHaveProperty('bar', 0);
expect(foo.reducer(undefined, baz())).toHaveProperty('bar', 1);
expect(foo.reducer(undefined, stopit())).toHaveProperty('bar', 0);
});

View File

@ -16,8 +16,7 @@ export function buildReducer(
defaultMutation?: Omit<MutationCase, 'matcher'>,
subduxes: Record<string, Dux> = {},
) {
// const subReducers =
// ? R.mapValues(subduxes, R.prop('reducer'));
const subReducers = R.mapValues(subduxes, R.prop('reducer'));
// TODO matcherMutation
// TODO defaultMutation
@ -48,6 +47,16 @@ export function buildReducer(
)(state);
}
if (!terminal && Object.keys(subduxes).length > 0) {
// subduxes
state = R.merge(
state,
R.mapValues(subReducers, (reducer, slice) =>
(reducer as any)(state[slice], action),
),
);
}
return state;
};