Skip to content

Instantly share code, notes, and snippets.

@gaearon
Last active December 11, 2020 14:56
Show Gist options
  • Save gaearon/0a2213881b5d53973514 to your computer and use it in GitHub Desktop.
Save gaearon/0a2213881b5d53973514 to your computer and use it in GitHub Desktop.
How I'd do code splitting in Redux (pseudo code, not tested!)
import { combineReducers } from 'redux';
import users from './reducers/users';
import posts from './reducers/posts';
export default function createReducer(asyncReducers) {
return combineReducers({
users,
posts,
...asyncReducers
});
}
import { injectAsyncReducer } from './store';
function createRoutes(store) {
// ...
const CommentsRoute = {
// ...
getComponents(location, callback) {
require.ensure([
'./pages/Comments',
'./reducers/comments'
], function (require) {
let Comments = require('./pages/Comments').default;
let commentsReducer = require('./reducers/comments').default;
injectAsyncReducer(store, 'comments', commentsReducer);
callback(null, Comments);
})
}
};
// ...
}
import { createStore } from 'redux';
import createReducer from './reducers';
export default function configureStore() {
let store = createStore(createReducer());
store.asyncReducers = {};
return store;
}
export function injectAsyncReducer(store, name, asyncReducer) {
store.asyncReducers[name] = reducer;
store.replaceReducer(createReducer(store.asyncReducers));
}
@dbismut
Copy link

dbismut commented Mar 27, 2016

Hey @gaearon, thanks for the code!

However, when rendering server side, the server would inject the store state to a client that is not yet aware of the async reducers.

And If I understand correctly, if a store state is passed properties that don't have a matching reducer, it simply "skips" the properties. Meaning that the store in the client will result missing the async reducers state.

So in order to make this work, I needed to create pseudo reducers that would eventually be replaced with the real async reducers. Such as:

// reducers.js
export default function createReducer(asyncReducers) {
  return combineReducers({
    users,
    posts,
    comments: (state = {}) => state, // <--- pseudo reducer
    ...asyncReducers
  });
}

EDIT: well it seems like I'm losing the default state from the async reducer... I'll investigate a bit more.

@WangLarry
Copy link

WangLarry commented Sep 6, 2016

for rendering server side, need change:

export function injectAsyncReducer(store, name, asyncReducer) {
  if (store.asyncReducers[name]) {
    // console.error(`inject ${name} reducer twice!`);
    return;
  }  
  store.asyncReducers[name] = reducer;
  store.replaceReducer(createReducer(store.asyncReducers));
}

@dlebedynskyi
Copy link

Does anyone have example of this working with SSR?

@vmehera123
Copy link

vmehera123 commented Feb 15, 2017

WEBPACK 2 example

here u r:

routes.js

  <Route
            getComponent={(nextState, cb) => {
              const name = "article",
                    page = System.import("./path/to/Page"),
                    reducer = System.import("./path/to/reducer");

              Promise
                .all([page, reducer])
                .then(result => {
                  const [page_component, async_reducer] = result;

                  injectAsyncReducer(store, name, async_reducer.default);
                  cb(null, page_component.default);
                });
            }}
            path="/path-to-something/"
          />

store.js (same, but only difference is pass initialState to check auth on server side and create store)

export default function configureStore(initialState) {
  const store = createStore(
    createReducer(),
    initialState,
    applyMiddleware(promiseMiddleware, thunk)
  );
  store.asyncReducers = {};

  return store;
}

serverMiddleware.js (update auth)

store = configureStore({auth: {
    user: req.user || null
  }});
...all_this_staff_with_match_etc

and yeah, u should include "auth" reducer in combineReducers before:

reducers.js

import auth from "./auth";

export default function createReducer(asyncReducers) {
  return combineReducers({
    auth,
    ...asyncReducers
  });
}

@HaNdTriX
Copy link

Just in case someone is interested.
I have implemented a running ssr example for next.js.

vercel/next.js#1459

@cvbuelow
Copy link

Here's a solution for injecting the initialState in async reducers, suggested by @gaearon
https://gist.github.com/cvbuelow/ea7bd4deb7824fdb65d81cd2b8c60f5e

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment