From 31c60600417bd720ada34aed9962d69c1f14774d Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Tue, 22 Oct 2019 18:10:45 -0400 Subject: [PATCH] add actions --- Changes | 7 +++++++ README.md | 32 +++++++++++++++++++++++++++++--- package.json | 2 +- src/actions.test.js | 18 ++++++++++++++++-- src/buildActions/index.js | 34 +++++++++++++++++++++++++++------- src/updux.js | 4 ++-- 6 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 Changes diff --git a/Changes b/Changes new file mode 100644 index 0000000..ae0a2f3 --- /dev/null +++ b/Changes @@ -0,0 +1,7 @@ +# Revision history for Updux + +0.0.2 2019-10-22 + - Add 'actions' in the config. + +0.0.1 2019-10-22 + - Initial release. diff --git a/README.md b/README.md index b26a577..31118b4 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,9 @@ const { next(action); }; }, + actions: { + customAction: ( someArg ) => ({ someProp: someArg }), + }, }); @@ -104,7 +107,6 @@ const rootUpdux = updux({ todos, statistics } }); -``` #### mutations @@ -187,7 +189,25 @@ updux({ } } }); + +### actions + +Generic action creations are automatically created from the mutations and effects, but you can +also define custom action creator here. The object's values are function that +transform the arguments of the creator to the action's payload. + ``` +const { actions } = updox({ + mutations: { + foo: () => state => state, + } + actions: { + bar: (x,y) => ({x,y}) + } +}); + +actions.foo({ x: 1, y: 2 }); // => { type: foo, payload: { x:1, y:2 } } +actions.bar(1,2); // => { type: bar, payload: { x:1, y:2 } } ## return value @@ -215,13 +235,19 @@ mutations will be performed first. ### actions -Action creators for all actions used in the mutations, effects and subdox +Action creators for all actions defined or used in the actions, mutations, effects and subduxes of the updox config. -The action creators have the signature `(payload={},meta={}) => ({type, +Non-custom action creators defined in `actions` have the signature `(payload={},meta={}) => ({type, payload,meta})` (with the extra sugar that if `meta` or `payload` are not specified, the key is not present in the produced action). +If the same action appears in multiple locations, the precedence order +determining which one will prevail is + + actions generated from mutations/effects < non-custom subduxes actions < + custom subduxes actions < custom actions + ### middleware A middleware aggregating all the effects defined in the diff --git a/package.json b/package.json index ad0cf36..6aa2228 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "build": "babel src --out-dir dist", "test": "jest" }, - "version": "0.0.1", + "version": "0.1.0", "repository": { "type": "git", "url": "git+https://github.com/yanick/updux.git" diff --git a/src/actions.test.js b/src/actions.test.js index b321e98..8c35f98 100644 --- a/src/actions.test.js +++ b/src/actions.test.js @@ -9,10 +9,18 @@ test( 'actions defined in effects and mutations, multi-level', () => { }, mutations: { bar: () => () => null }, subduxes: { - mysub: updux({ + mysub: { effects: { baz: api => next => action => { }, }, mutations: { quux: () => () => null }, - }) + actions: { + foo: (limit) => ({limit}), + }, + }, + myothersub: { + effects: { + foo: () => () => () => {}, + }, + } }, }); @@ -21,4 +29,10 @@ test( 'actions defined in effects and mutations, multi-level', () => { expect( types).toEqual([ 'bar', 'baz', 'foo', 'quux', ]); + expect( actions.bar() ).toEqual({ type: 'bar' }); + expect( actions.bar('xxx') ).toEqual({ type: 'bar', payload: 'xxx' }); + expect( actions.bar(undefined,'yyy') ).toEqual({ type: 'bar',meta: 'yyy' }); + + expect(actions.foo(12)).toEqual({type: 'foo', payload: { limit: 12 }}); + }); diff --git a/src/buildActions/index.js b/src/buildActions/index.js index 120a10e..c3b4848 100644 --- a/src/buildActions/index.js +++ b/src/buildActions/index.js @@ -1,20 +1,40 @@ import fp from 'lodash/fp'; function actionFor(type) { - return ( (payload = undefined, meta = undefined) => - fp.pickBy(v => v !== null)({type, payload, meta}) + const creator = ( (payload = undefined, meta = undefined) => + fp.pickBy(v => v !== undefined)({type, payload, meta}) ); + + creator._genericAction = true; + + return creator; } export default function buildActions( + creators = {}, mutations = {}, effects = {}, - subActions = {}, + subActions = [], ) { - return { ...subActions, - ...fp.fromPairs([ ...Object.keys(mutations), ...Object.keys(effects) ] - .map( type => [ type, actionFor(type) ])) - }; + // priority => generics => generic subs => craft subs => creators + + const [ crafted, generic ] = fp.partition( + ([type,f]) => !f._genericAction + )( fp.flatten( subActions.map( x => Object.entries(x) ) ).filter( + ([_,f]) => f + ) ) + + const actions = [ + ...([ ...Object.keys(mutations), ...Object.keys(effects) ] + .map( type => [ type, actionFor(type) ] )), + ...generic, + ...crafted, + ...Object.entries(creators).map( + ([type, payload]) => [type, (...args) => ({ type, payload: payload(...args) })] + ), + ]; + + return fp.fromPairs(actions); } diff --git a/src/updux.js b/src/updux.js index 08a0b31..44d6303 100644 --- a/src/updux.js +++ b/src/updux.js @@ -17,10 +17,10 @@ export class Updux { this.actions = buildActions( + config.actions, config.mutations, config.effects, - fp.mergeAll( Object.values( this.subduxes ).map( ({ actions }) => - actions ) ) + Object.values( this.subduxes ).map( ({actions}) => actions ), ) this.initial = buildInitial(