From f3af5458600a70f487406000676a49adc309a737 Mon Sep 17 00:00:00 2001 From: "M.K. Safi" Date: Thu, 28 Jan 2016 12:27:13 -0800 Subject: [PATCH] Fix unknown action type handling. --- src/combineReducers.js | 18 +++----------- src/createStore.js | 2 +- test/combineReducers.spec.js | 47 ++++++++++++++++-------------------- 3 files changed, 25 insertions(+), 42 deletions(-) diff --git a/src/combineReducers.js b/src/combineReducers.js index 4929654193..d4cc63f983 100644 --- a/src/combineReducers.js +++ b/src/combineReducers.js @@ -57,21 +57,9 @@ function assertReducerSanity(reducers) { if (typeof initialState === 'undefined') { throw new Error( `Reducer "${key}" returned undefined during initialization. ` + - `If the state passed to the reducer is undefined, you must ` + - `explicitly return the initial state. The initial state may ` + - `not be undefined.` - ) - } - - var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.') - if (typeof reducer(undefined, { type }) === 'undefined') { - throw new Error( - `Reducer "${key}" returned undefined when probed with a random type. ` + - `Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` + - `namespace. They are considered private. Instead, you must return the ` + - `current state for any unknown actions, unless it is undefined, ` + - `in which case you must return the initial state, regardless of the ` + - `action type. The initial state may not be undefined.` + `If the state passed to the reducer is undefined or the action passed to the reducer ` + + `has an unknown type, you must explicitly return the initial state. The initial state ` + + `may not be undefined.` ) } }) diff --git a/src/createStore.js b/src/createStore.js index 082fad8ec0..80c9ee27e3 100644 --- a/src/createStore.js +++ b/src/createStore.js @@ -7,7 +7,7 @@ import isPlainObject from './utils/isPlainObject' * Do not reference these action types directly in your code. */ export var ActionTypes = { - INIT: '@@redux/INIT' + INIT: '@@redux/INIT_' + Math.random().toString(36).substring(7).split('').join('.') } /** diff --git a/test/combineReducers.spec.js b/test/combineReducers.spec.js index a2b00857d0..0e23f43dee 100644 --- a/test/combineReducers.spec.js +++ b/test/combineReducers.spec.js @@ -1,6 +1,6 @@ import expect from 'expect' import { combineReducers } from '../src' -import createStore, { ActionTypes } from '../src/createStore' +import createStore from '../src/createStore' describe('Utils', () => { describe('combineReducers', () => { @@ -66,7 +66,8 @@ describe('Utils', () => { ) }) - it('throws an error on first call if a reducer returns undefined initializing', () => { + it('throws an error on first call if a reducer could potentially return undefined when ' + + 'undefined state is passed to it', () => { const reducer = combineReducers({ counter(state, action) { switch (action.type) { @@ -79,9 +80,24 @@ describe('Utils', () => { } } }) - expect(() => reducer({ })).toThrow( - /"counter".*initialization/ - ) + + expect(() => reducer(0, { type: 'increment' })).toThrow(/"counter".*initialization/) + }) + + it('throws an error on first call if a reducer could potentially return undefined for ' + + 'unknown action types', () => { + const reducer = combineReducers({ + counter(state = 0, action) { + switch (action.type) { + case 'increment': + return state + 1 + case 'decrement': + return state - 1 + } + } + }) + + expect(() => reducer(0, { type: 'increment' })).toThrow(/"counter".*initialization/) }) it('catches error thrown in reducer when initializing and re-throw', () => { @@ -151,27 +167,6 @@ describe('Utils', () => { expect(reducer(initialState, { type: 'increment' })).toNotBe(initialState) }) - it('throws an error on first call if a reducer attempts to handle a private action', () => { - const reducer = combineReducers({ - counter(state, action) { - switch (action.type) { - case 'increment': - return state + 1 - case 'decrement': - return state - 1 - // Never do this in your code: - case ActionTypes.INIT: - return 0 - default: - return undefined - } - } - }) - expect(() => reducer()).toThrow( - /"counter".*private/ - ) - }) - it('warns if no reducers are passed to combineReducers', () => { const spy = expect.spyOn(console, 'error') const reducer = combineReducers({ })