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

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