add sink mutations

This commit is contained in:
Yanick Champoux 2019-11-05 13:06:17 -05:00
parent 73973a9588
commit 0e03628502
3 changed files with 105 additions and 49 deletions

View File

@ -12,7 +12,7 @@ type SubMutations = {
} }
function buildMutations( function buildMutations(
mutations :Dictionary<Mutation> = {}, mutations :Dictionary<Mutation|([Mutation,boolean|undefined])> = {},
subduxes = {} subduxes = {}
) { ) {
// we have to differentiate the subduxes with '*' than those // we have to differentiate the subduxes with '*' than those
@ -49,15 +49,26 @@ function buildMutations(
nonGlobby.forEach(([slice, {mutations = {}, reducer = {}}]:any[]) => { nonGlobby.forEach(([slice, {mutations = {}, reducer = {}}]:any[]) => {
Object.entries(mutations).forEach(([type, mutation]) => { Object.entries(mutations).forEach(([type, mutation]) => {
const localized = (payload = null, action :Action) => const localized = (payload = null, action :Action) => {
u.updateIn(slice)((mutation as Mutation)(payload, action)); console.log(slice);
return u.updateIn(slice)((mutation as Mutation)(payload, action));
}
mergedMutations[type].push(localized); mergedMutations[type].push(localized);
}); });
}); });
Object.entries(mutations).forEach(([type, mutation]) => { Object.entries(mutations).forEach(([type, mutation]) => {
mergedMutations[type].push(mutation); if ( Array.isArray(mutation) ) {
if( mutation[1] ) {
mergedMutations[type] = [
mutation[0]
]
}
else mergedMutations[type].push( mutation[0] );
}
else mergedMutations[type].push(mutation);
}); });
return fp.mapValues(composeMutations)(mergedMutations); return fp.mapValues(composeMutations)(mergedMutations);

39
src/sink.test.ts Normal file
View File

@ -0,0 +1,39 @@
import Updux from './updux';
const foo = new Updux<number>({
initial: 0,
mutations: {
doIt: () => (state: number) => {
console.log(state);
return state + 1;
},
},
});
const bar = new Updux<{foo: number}>({
subduxes: {foo},
mutations: {
doIt: () => (state: any) => state,
},
});
bar.addMutation(
foo.actions.doIt,
() => (state: any) => ({...state, bar: 'yay'}),
true,
);
test('initial', () => {
expect(bar.initial).toEqual({foo: 0});
});
test('foo alone', () => {
expect(foo.reducer(undefined, foo.actions.doIt())).toEqual(1);
});
test('sink mutations', () => {
expect(bar.reducer(undefined, bar.actions.doIt())).toEqual({
foo: 0,
bar: 'yay',
});
});

View File

@ -42,12 +42,12 @@ export type Dux<S> = Pick<
>; >;
/** /**
* `Updux` is a way to minimize and simplify the boilerplate associated with the * `Updux` is a way to minimize and simplify the boilerplate associated with the
* creation of a `Redux` store. It takes a shorthand configuration * creation of a `Redux` store. It takes a shorthand configuration
* object, and generates the appropriate reducer, actions, middleware, etc. * object, and generates the appropriate reducer, actions, middleware, etc.
* In true `Redux`-like fashion, upduxes can be made of sub-upduxes (`subduxes` for short) for different slices of the root state. * In true `Redux`-like fashion, upduxes can be made of sub-upduxes (`subduxes` for short) for different slices of the root state.
* @typeparam S Store's state type. Defaults to `any`. * @typeparam S Store's state type. Defaults to `any`.
*/ */
export class Updux<S = any> { export class Updux<S = any> {
subduxes: Dictionary<Updux>; subduxes: Dictionary<Updux>;
@ -60,41 +60,41 @@ export class Updux<S = any> {
*/ */
initial: S; initial: S;
/** /**
* Function that can be provided to alter all local mutations of the updux * Function that can be provided to alter all local mutations of the updux
* (the mutations of subduxes are left untouched). * (the mutations of subduxes are left untouched).
* *
* Can be used, for example, for Immer integration: * Can be used, for example, for Immer integration:
* *
* ``` * ```
* import Updux from 'updux'; * import Updux from 'updux';
* import { produce } from 'Immer'; * import { produce } from 'Immer';
* *
* const updux = new Updux({ * const updux = new Updux({
* initial: { counter: 0 }, * initial: { counter: 0 },
* groomMutations: mutation => (...args) => produce( mutation(...args) ), * groomMutations: mutation => (...args) => produce( mutation(...args) ),
* mutations: { * mutations: {
* add: (inc=1) => draft => draft.counter += inc * add: (inc=1) => draft => draft.counter += inc
* } * }
* }); * });
* *
* ``` * ```
* *
* Or perhaps for debugging: * Or perhaps for debugging:
* *
* ``` * ```
* import Updux from 'updux'; * import Updux from 'updux';
* *
* const updux = new Updux({ * const updux = new Updux({
* initial: { counter: 0 }, * initial: { counter: 0 },
* groomMutations: mutation => (...args) => state => { * groomMutations: mutation => (...args) => state => {
* console.log( "got action ", args[1] ); * console.log( "got action ", args[1] );
* return mutation(...args)(state); * return mutation(...args)(state);
* } * }
* }); * });
* *
* ``` * ```
*/ */
groomMutations: (mutation: Mutation<S>) => Mutation<S>; groomMutations: (mutation: Mutation<S>) => Mutation<S>;
@observable private localEffects: Dictionary< @observable private localEffects: Dictionary<
@ -103,7 +103,9 @@ export class Updux<S = any> {
@observable private localActions: Dictionary<ActionCreator>; @observable private localActions: Dictionary<ActionCreator>;
@observable private localMutations: Dictionary<Mutation<S>>; @observable private localMutations: Dictionary<
Mutation<S> | [Mutation<S>, boolean | undefined]
>;
constructor(config: UpduxConfig = {}) { constructor(config: UpduxConfig = {}) {
this.groomMutations = config.groomMutations || ((x: Mutation<S>) => x); this.groomMutations = config.groomMutations || ((x: Mutation<S>) => x);
@ -245,6 +247,8 @@ export class Updux<S = any> {
* Adds a mutation and its associated action to the updux. * Adds a mutation and its associated action to the updux.
* If a local mutation was already associated to the action, * If a local mutation was already associated to the action,
* it will be replaced by the new one. * it will be replaced by the new one.
* @param isSink
* If `true`, disables the subduxes mutations for this action.
* @example * @example
* ``` * ```
* updux.addMutation( add, inc => state => state + inc ); * updux.addMutation( add, inc => state => state + inc );
@ -253,11 +257,13 @@ export class Updux<S = any> {
addMutation<A extends ActionCreator>( addMutation<A extends ActionCreator>(
creator: A, creator: A,
mutation: Mutation<S, A extends (...args: any[]) => infer R ? R : never>, mutation: Mutation<S, A extends (...args: any[]) => infer R ? R : never>,
isSink?: boolean,
) { ) {
this.localActions[creator.type] = creator; this.localActions[creator.type] = creator;
this.localMutations[creator.type] = this.groomMutations( this.localMutations[creator.type] = [
mutation as any, this.groomMutations(mutation as any) as Mutation<S>,
) as Mutation<S>; isSink,
];
} }
} }