React-Redux: Combining reducers: Unexpected Keys

My app works fine before I start to combine my Redux reducers. But when I combine, the initialState and reducer keys get mixed up.

function flash(state = [], action) {
  switch (action.type) {
  case FLASH_MESSAGE_UPDATED:
    return _.extend({}, state, { flash: action.flash })
  default:
    return state
  }
}

function events(state = [], action) {
  switch (action.type) {
  case EVENTS_UPDATED:
    return _.extend({}, state, { events: action.pathway_events })
  default:
    return state
  }
}

export default combineReducers({
  events,
  flash
})

This results in broken functionality and a console error of:

Unexpected keys "one", "two" found in initialState argument passed to createStore. Expected to find one of the known reducer keys instead: "events", "flash". Unexpected keys will be ignored.

My initial state is passed in with the help of redux-thunk.

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from '../../reducers/event'

let initialState = {
  one: globalData.one,
  two: globalData.two,
  events: globalData.events,
  flash: globalData.flash
}
let createStoreWithMiddleware = applyMiddleware(thunk)(createStore)
let reduxStore = createStoreWithMiddleware(reducer, initialState);

React.render(
  <Provider store={reduxStore}>
    <EventListContainer />
  </Provider>,
  $('.events')[0]
)

How can I correctly combine reducers?


Solution 1:

I think you simply need to add reducers for the additional keys, e.g.

function one(state = {}, action) {
  switch (action.type) {
  case ONE_UPDATED:
    return action.one
  default:
    return state
  }
}

From the docs:

If you produced reducer with combineReducers, this must be a plain object with the same shape as the keys passed to it. Otherwise, you are free to pass anything that your reducer can understand.

If you don't need to handle any actions related to one or two, just pull them in initially, this could be as simple as

export default combineReducers({
  events,
  flash,
  one: (state = {}) => state,
  two: (state = {}) => state
})

Solution 2:

tl;dr

If you do SSR, recompile your server side bundle!

Explanation

This error message can appear when you do server side rendering (SSR), change something in the reducer's code and you only recompile/HMR it on the client side.

When you do SSR, you have to serialize your Redux store to a global variable (like window.__INITIAL_STATE__), so when you initialize the client side, createStore can read that and build the same Redux state.

If you don't recompile your modified code for the server side, the initial state from the server may still contain a state with the old keys (from the old reducers) while the client side has the new state (from the new/modified reducers).

Technically this won't break how the client side works because Redux ignores the unexpected keys, its just a useful warning (not really an error) that remembers you to recompile your server side bundle. Tho, this can be a concern in production or when you benchmark hydration performance because the different state can result in a different DOM. Of course, this mistake shouldn't happen in production, because your deployment process should automatically create both the client and server side bundle.