How is it possible to have both a preloadedState (hydrating from server) and inject reducers dynamically?
In react-boilerplate
or How to dynamically load reducers for code splitting in a Redux application? there is the concept of reducers which are added dynamically, based on the page/components you are viewing. Extract from reducers.js
:
export default function createReducer(asyncReducers) {
return combineReducers({
users,
posts,
...asyncReducers
});
}
While this works well when navigating from one page to another (or on a client-side only application); when hydrating data from the server I am encountering this error:
Unexpected property "comments" found in previous state received by the reducer. Expected to find one of the known reducer property names instead: "users", "posts". Unexpected properties will be ignored.
(where comments
is the property name of the dynamically injected reducer)
The reason for this error is clear, since the preloadedState
coming from the server (using React SSR) already contains comments
and the initial reducer does not since this is added dynamically afterwards. The error disappears if I add comments
to my combineReducers
; however that means that at app initialisation I need to include all reducers; which is not ideal.
You should be able to use dummy reducers in place of dynamically loaded reducers which will be replaced when the real reducers are loaded.
{ comments: (state = null) => state }
This can also be done automatically, depending on your implementation, as per http://nicolasgallagher.com/redux-modules-and-code-splitting/
// Preserve initial state for not-yet-loaded reducers
const combine = (reducers) => {
const reducerNames = Object.keys(reducers);
Object.keys(initialState).forEach(item => {
if (reducerNames.indexOf(item) === -1) {
reducers[item] = (state = null) => state;
}
});
return combineReducers(reducers);
};