diff --git a/README.md b/README.md index 7d365f7..c3655ad 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ type Map = { ## Usage +`npm install --save redux-combine-containers` + Let's say we have 2 react-redux containers, `document-viewer` and `document-list`. These module can be used standalone with their own state, actions and views. We want to create a new `document-index` container, using these two submodules, with a single state tree, without duplicating code. diff --git a/__tests__/index.test.js b/__tests__/index.test.js index 6c4e901..f817b77 100644 --- a/__tests__/index.test.js +++ b/__tests__/index.test.js @@ -1,7 +1,7 @@ //@flow import { fromJS } from "immutable"; -import { getCombinedReducer, getCombinedDispatchToProps, getCombinedStateToProps } from "../esm/index.js"; +import { getCombinedReducer, getCombinedDispatchToProps, getCombinedStateToProps } from "../index.js"; test("getCombinedReducer", () => { const reducers = { diff --git a/esm/index.js b/esm/index.js index 304aa7e..da7ab30 100644 --- a/esm/index.js +++ b/esm/index.js @@ -1,43 +1,28 @@ -//@flow +import _Object$assign from '@babel/runtime/core-js/object/assign'; +import _Object$entries from '@babel/runtime/core-js/object/entries'; + /** * Helpers to combine multiple redux containers props object (mapStateToProps/mapDispatchToProps pairs) * into a single props object. * Each individual container is identified by a key in the final object. */ - /** - * mapStateToProps / mapDispatch to props pair. - */ -export type CombinedMapper = { - mapStateToProps: (state: Map) => any, - mapDispatchToProps: (dispatch: any, props: any) => any -}; +/** + * mapStateToProps / mapDispatch to props pair. + */ /** * Simple definition of immutable.Map */ -type Map = { - get: (key: any) => any, - set: (key: any, value: any) => Map -}; - -type ReduxAction = { - type: string, - data?: any -}; /** * Naive object reduce implementation to avoid dependencies */ -const reduceMappers = (mappers: {[key: string]: any}, fn: (accumulator: any, value: any) => any, initialValue: any) => { - return Object.entries(mappers).reduce( - (acc, pair: Array) => { - return fn.call(null, acc, pair[1], pair[0]); - }, - initialValue - ); +const reduceMappers = (mappers, fn, initialValue) => { + return _Object$entries(mappers).reduce((acc, pair) => { + return fn.call(null, acc, pair[1], pair[0]); + }, initialValue); }; - /** * This is essentially `combineReducers`: each sub-reducer gets his sub-state tree only. * @@ -45,22 +30,19 @@ const reduceMappers = (mappers: {[key: string]: any}, fn: (accumulator: any, val * * If a reducer has a `global` key, this reducer will have access to the complete state tree. */ -export const getCombinedReducer = (reducers: {[key: string]: (state: Map) => Map}) => { - return (state: Map, action: ReduxAction): Map => { - return reduceMappers( - reducers, - (curState: Map, reducer: (state: Map) => Map, key: string) => { - if (key === "global") { - return reducer(curState, action); - } - return curState.set(key, reducer(curState.get(key), action)); - }, - state - ); - }; -}; +const getCombinedReducer = reducers => { + return (state, action) => { + return reduceMappers(reducers, (curState, reducer, key) => { + if (key === "global") { + return reducer(curState, action); + } + + return curState.set(key, reducer(curState.get(key), action)); + }, state); + }; +}; /** * The returned object (the final state tree) is a combination of `globalProps` with each sub-state trees. * @@ -69,25 +51,20 @@ export const getCombinedReducer = (reducers: {[key: string]: (state: Map) => Map * * `globalProps` are at the root of the state tree. */ -export const getCombinedStateToProps = (mappers: {[key: string]: CombinedMapper}, state: Map, globalProps: any) => { - return reduceMappers( - mappers, - (props: any, mapper: any, key: string) => { - props[ key ] = props[ key ] ? props[ key ] : {}; - props[ key ] = Object.assign(props[ key ], mapper.mapStateToProps(state.get(key))); - return props; - }, - globalProps - ); + +const getCombinedStateToProps = (mappers, state, globalProps) => { + return reduceMappers(mappers, (props, mapper, key) => { + props[key] = props[key] ? props[key] : {}; + props[key] = _Object$assign(props[key], mapper.mapStateToProps(state.get(key))); + return props; + }, globalProps); +}; +const getCombinedDispatchToProps = (mappers, dispatch, props, defaultActions) => { + return reduceMappers(mappers, (props, mapper, key) => { + props[key] = props[key] ? props[key] : {}; + props[key] = _Object$assign(props[key], mapper.mapDispatchToProps(dispatch)); + return props; + }, defaultActions); }; -export const getCombinedDispatchToProps = (mappers: {[key: string]: CombinedMapper}, dispatch: () => void, props: any, defaultActions: any) => { - return reduceMappers( - mappers, - (props: any, mapper: any, key: string) => { - props[ key ] = props[ key ] ? props[ key ] : {}; - props[ key ] = Object.assign(props[ key ], mapper.mapDispatchToProps(dispatch)); - return props; - }, - defaultActions - ); -}; \ No newline at end of file + +export { getCombinedReducer, getCombinedStateToProps, getCombinedDispatchToProps }; diff --git a/index.js b/index.js new file mode 100644 index 0000000..304aa7e --- /dev/null +++ b/index.js @@ -0,0 +1,93 @@ +//@flow +/** + * Helpers to combine multiple redux containers props object (mapStateToProps/mapDispatchToProps pairs) + * into a single props object. + * Each individual container is identified by a key in the final object. + */ + + /** + * mapStateToProps / mapDispatch to props pair. + */ +export type CombinedMapper = { + mapStateToProps: (state: Map) => any, + mapDispatchToProps: (dispatch: any, props: any) => any +}; + +/** + * Simple definition of immutable.Map + */ +type Map = { + get: (key: any) => any, + set: (key: any, value: any) => Map +}; + +type ReduxAction = { + type: string, + data?: any +}; + +/** + * Naive object reduce implementation to avoid dependencies + */ +const reduceMappers = (mappers: {[key: string]: any}, fn: (accumulator: any, value: any) => any, initialValue: any) => { + return Object.entries(mappers).reduce( + (acc, pair: Array) => { + return fn.call(null, acc, pair[1], pair[0]); + }, + initialValue + ); +}; + +/** + * This is essentially `combineReducers`: each sub-reducer gets his sub-state tree only. + * + * Example: "document" reducer : `state.set("document", reducers.document(state.get("document"), action));` + * + * If a reducer has a `global` key, this reducer will have access to the complete state tree. + */ +export const getCombinedReducer = (reducers: {[key: string]: (state: Map) => Map}) => { + return (state: Map, action: ReduxAction): Map => { + return reduceMappers( + reducers, + (curState: Map, reducer: (state: Map) => Map, key: string) => { + if (key === "global") { + return reducer(curState, action); + } + return curState.set(key, reducer(curState.get(key), action)); + }, + state + ); + }; +}; + + +/** + * The returned object (the final state tree) is a combination of `globalProps` with each sub-state trees. + * + * Each submodule has it's key in the state tree. + * `finalProps.document = mappers.document.mapStateToProps(state)` + * + * `globalProps` are at the root of the state tree. + */ +export const getCombinedStateToProps = (mappers: {[key: string]: CombinedMapper}, state: Map, globalProps: any) => { + return reduceMappers( + mappers, + (props: any, mapper: any, key: string) => { + props[ key ] = props[ key ] ? props[ key ] : {}; + props[ key ] = Object.assign(props[ key ], mapper.mapStateToProps(state.get(key))); + return props; + }, + globalProps + ); +}; +export const getCombinedDispatchToProps = (mappers: {[key: string]: CombinedMapper}, dispatch: () => void, props: any, defaultActions: any) => { + return reduceMappers( + mappers, + (props: any, mapper: any, key: string) => { + props[ key ] = props[ key ] ? props[ key ] : {}; + props[ key ] = Object.assign(props[ key ], mapper.mapDispatchToProps(dispatch)); + return props; + }, + defaultActions + ); +}; \ No newline at end of file diff --git a/rollup.config.js b/rollup.config.js index fb95991..502da08 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -3,15 +3,18 @@ import replace from "rollup-plugin-replace"; export default { - input: "./esm/index.js", + input: "./index.js", output: [ { file: "./cjs/index.js", format: "cjs" + },{ + file: "./esm/index.js", + format: "es" } ], watch: { - include: ["./esm/index.js"] + include: ["./index.js"] }, plugins: [ replace({