diff --git a/API.md b/API.md index 422d52b..1a42bfa 100644 --- a/API.md +++ b/API.md @@ -28,8 +28,9 @@ Returns an uninitialized application: - `app.store` - the redux store returned from the application's `createStore` config. - `app.getState()` - an alias to `app.store.getState()`. - `app.dispatch()` - an alias to `app.store.dispatch()`. - - `app.dispatch.MOD.ACTION()` - dispatches `MOD`'s action named `ACTION`, passing-along arguments to that action. Same as `app.dispatch(app.actions.MOD.ACTION())`. + - `app.dispatch.MOD.ACTION()` - dispatches `MOD`'s action named `ACTION`, passing along arguments to that action. Same as `app.dispatch(app.actions.MOD.ACTION())`. - `app.actions.MOD.ACTION()` - an alias for `MOD`'s action named `ACTION`. Same as `app.mods.MOD.actions.ACTION()`. + - `app.select.MOD.SELECTOR()` - calls `MOD`'s selector named `SELECTOR` with the app's current state bound as the first argument and passing along additional arguments. Same as `app.mods.MOD.selectors.SELECTOR(app.getState())`. - `app.selectors.MOD.SELECTOR()` - an alias for `MOD`'s selector named `SELECTOR`. Same as `app.mods.MOD.selectors.SELECTOR()`. #### `middleware` diff --git a/README.md b/README.md index 46b1521..7b4cded 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ const MiddleEnd = require('strange-middle-end'); (async () => { - const { FETCH_USER, INCREMENT } = MiddleEnd.createTypes({ + const { INCREMENT, FETCH_USER } = MiddleEnd.createTypes({ INCREMENT: MiddleEnd.type.simple, FETCH_USER: MiddleEnd.type.async }); @@ -142,7 +142,7 @@ const MiddleEnd = require('strange-middle-end'); await app.dispatch.model.fetchUser({ id: 42 }); - console.log(app.selectors.model.getUser(app.getState())); + console.log(app.select.model.getUser()); app.dispatch.counter.increment(); diff --git a/lib/core.js b/lib/core.js index 25e5bec..6ecfe6c 100644 --- a/lib/core.js +++ b/lib/core.js @@ -25,6 +25,7 @@ exports.create = ({ createStore, mods = {} }) => { middleEnd.getState; middleEnd.dispatch; middleEnd.actions; + middleEnd.select; middleEnd.selectors; // Setup any schemas @@ -118,6 +119,19 @@ exports.create = ({ createStore, mods = {} }) => { return internals.pickEach(middleEnd.mods, 'actions'); }); + initializedProp('select', () => { + + const { selectors, getState } = middleEnd; + + // Convenient selection, + // select.auth.isAuthenticated() versus selectors.auth.isAuthenticated(getState()) + + return internals.mapEachLeafFunction(selectors, (selector) => { + + return (...args) => selector(getState(), ...args); + }); + }); + initializedProp('selectors', () => { return internals.pickEach(middleEnd.mods, 'selectors'); @@ -167,3 +181,18 @@ internals.pickEach = (obj, prop) => { .filter(([, value]) => value && typeof value[prop] !== 'undefined') .reduce((collect, [key, value]) => ({ ...collect, [key]: value[prop] }), {}); }; + +internals.mapEachLeafFunction = (obj, map) => { + + return Object.entries(obj).reduce((collect, [key, value]) => { + + return { + ...collect, + [key]: typeof value === 'function' ? + map(value) : + (value && typeof value === 'object') ? + internals.mapEachLeafFunction(value, map) : + value + }; + }, {}); +}; diff --git a/test/core.js b/test/core.js index 9f78371..6e41825 100644 --- a/test/core.js +++ b/test/core.js @@ -347,6 +347,51 @@ describe('Core', () => { expect(m.selectors.a).to.shallow.equal(m.mods.a.selectors); expect(m.selectors.c).to.shallow.equal(m.mods.c.selectors); }); + + it('has state-bound selectors from mod branches.', () => { + + const m = MiddleEnd.create({ + mods: { + a: { + selectors: { + getUpper: ({ a }) => a.toUpperCase() + } + }, + b: {}, + c: { + selectors: { + multiply: ({ c }, y) => c * y, + some: 'value', + group: { + null: null, + manyArgs: ({ c }, three, four, five) => `${c}${three}${four}${five}` + } + } + }, + d: null + }, + createStore: () => { + + return Redux.createStore(() => ({ + a: 'a', + c: 2 + })); + } + }); + + m.initialize(); + + expect(m.select).to.only.contain(['a', 'c']); + expect(m.select.a).to.only.contain(['getUpper']); + expect(m.select.c).to.only.contain(['multiply', 'some', 'group']); + expect(m.select.c.group).to.only.contain(['null', 'manyArgs']); + + expect(m.select.a.getUpper()).to.equal('A'); + expect(m.select.c.multiply(15)).to.equal(30); + expect(m.select.c.some).to.equal('value'); + expect(m.select.c.group.null).to.equal(null); + expect(m.select.c.group.manyArgs(3, 4, 5)).to.equal('2345'); + }); }); describe('middleware', () => {