Skip to content

Commit

Permalink
Add optional “options” param to combineReducers and add example test …
Browse files Browse the repository at this point in the history
…using immutable-js
  • Loading branch information
borkxs committed Jun 8, 2016
1 parent 61856f0 commit 7b3315e
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 5 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
"expect": "^1.8.0",
"gitbook-cli": "^0.3.4",
"glob": "^6.0.4",
"immutable": "3.8.1",
"isparta": "^4.0.0",
"mocha": "^2.2.5",
"rimraf": "^2.3.4",
Expand Down
27 changes: 22 additions & 5 deletions src/combineReducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,26 @@ function assertReducerSanity(reducers) {
* if the state passed to them was undefined, and the current state for any
* unrecognized action.
*
* @param {Object} [options={
* create: (obj) => obj,
* get: (state, key) => state[key],
* set: (state, key, value) => {
* state[key] = value
* return state
* }
* }] An optional object for configuring how the top-level state is managed.
*
* @returns {Function} A reducer function that invokes every reducer inside the
* passed object, and builds a state object with the same shape.
*/
export default function combineReducers(reducers) {
export default function combineReducers(reducers, options = {
create: (obj) => obj,
get: (state, key) => state[key],
set: (state, key, stateForKey) => {
state[key] = stateForKey
return state
},
}) {
var reducerKeys = Object.keys(reducers)
var finalReducers = {}
for (var i = 0; i < reducerKeys.length; i++) {
Expand All @@ -99,6 +115,7 @@ export default function combineReducers(reducers) {
finalReducers[key] = reducers[key]
}
}

var finalReducerKeys = Object.keys(finalReducers)

var sanityError
Expand All @@ -108,7 +125,7 @@ export default function combineReducers(reducers) {
sanityError = e
}

return function combination(state = {}, action) {
return function combination(state = options.create({}), action) {
if (sanityError) {
throw sanityError
}
Expand All @@ -121,17 +138,17 @@ export default function combineReducers(reducers) {
}

var hasChanged = false
var nextState = {}
var nextState = options.create({})
for (var i = 0; i < finalReducerKeys.length; i++) {
var key = finalReducerKeys[i]
var reducer = finalReducers[key]
var previousStateForKey = state[key]
var previousStateForKey = options.get(state, key)
var nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
var errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
nextState = options.set(nextState, key, nextStateForKey)
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
Expand Down
22 changes: 22 additions & 0 deletions test/combineReducers.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import expect from 'expect'
import Immutable from 'immutable'
import { combineReducers } from '../src'
import createStore, { ActionTypes } from '../src/createStore'

Expand All @@ -18,6 +19,27 @@ describe('Utils', () => {
expect(s2).toEqual({ counter: 1, stack: [ 'a' ] })
})

it('accepts options that configure how the combined reducer state is managed', () => {
const reducer = combineReducers({
counter: (state = 0, action) =>
action.type === 'increment' ? state + 1 : state,
stack: (state = [], action) =>
action.type === 'push' ? [ ...state, action.value ] : state
}, {
create: (obj) =>
(obj instanceof Immutable.Map) ? obj : new Immutable.Map(obj),
get: (state, key) => state.get(key),
set: (state, key, value) => state.set(key, value)
})

const s1 = reducer(undefined, { type: 'increment' })
expect(s1.get('counter')).toEqual(1)
expect(s1.get('stack')).toEqual([])
const s2 = reducer(s1, { type: 'push', value: 'a' })
expect(s2.get("counter")).toEqual(1)
expect(s2.get("stack")).toEqual([ 'a' ])
})

it('ignores all props which are not a function', () => {
const reducer = combineReducers({
fake: true,
Expand Down

0 comments on commit 7b3315e

Please sign in to comment.