Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #1518: Move root route configuration to routes.js, add documentation for creating a global saga #1545

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 1 addition & 8 deletions app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ import FontFaceObserver from 'fontfaceobserver';
import useScroll from 'react-router-scroll/lib/useScroll';
import 'sanitize.css/sanitize.css';

// Import root app
import App from 'containers/App';

// Import selector for `syncHistoryWithStore`
import { makeSelectLocationState } from 'containers/App/selectors';

Expand Down Expand Up @@ -84,18 +81,14 @@ const history = syncHistoryWithStore(browserHistory, store, {
});

// Set up the router, wrapping all Routes in the App component
const rootRoute = {
component: App,
childRoutes: createRoutes(store),
};

const render = (messages) => {
ReactDOM.render(
<Provider store={store}>
<LanguageProvider messages={messages}>
<Router
history={history}
routes={rootRoute}
routes={createRoutes(store)}
render={
// Scroll to top when going to a new page, imitating default browser
// behaviour
Expand Down
11 changes: 10 additions & 1 deletion app/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default function createRoutes(store) {
// create reusable async injectors using getAsyncInjectors factory
const { injectReducer, injectSagas } = getAsyncInjectors(store);

return [
const childRoutes = [
{
path: '/',
getComponent(nextState, cb) {
Expand Down Expand Up @@ -53,4 +53,13 @@ export default function createRoutes(store) {
},
},
];

return {
getComponent(nextState, cb) {
import('containers/App')
.then(loadModule(cb))
.catch(errorLoading);
},
childRoutes,
};
}
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- [ImmutableJS](js/immutablejs.md)
- [reselect](js/reselect.md)
- [redux-saga](js/redux-saga.md)
- [global saga](js/global-saga.md)
- [i18n](js/i18n.md)
- [routing](js/routing.md)
- [Maintenance](maintenance)
Expand Down
1 change: 1 addition & 0 deletions docs/js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ add new parts of your application!
- [ImmutableJS](immutablejs.md)
- [reselect](reselect.md)
- [redux-saga](redux-saga.md)
- [global saga](global-saga.md)
- [react-intl](i18n.md)
- [routing](routing.md)

Expand Down
63 changes: 63 additions & 0 deletions docs/js/global-saga.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# `global saga`

A global saga is an app level saga. The built-in example of sagas in this app are all contained within child containers that get put inside the `App` container. The app container by itself is pretty basic, allowing for all the action to happen within it's child containers; however, sometimes that's just not enough. Imagine the following scenario if you will.

> You have an app that upon loading needs to grab data from the server, regardless of which route the user entered. You could put a function inside the `onComponentMount` of every single container, but that's not very DRY. What you end up with is a lot of duplicate constants, actions, and reducers.
>
> Enter the global saga. A way to load up a `sagas.js` file for the `App` container.

## Usage

We'll be doing the following:

1. Adding `sagas.js` to `app/containers/App` directory
2. importing `sagas.js` into the App container route configuration

Like other sagas, your global saga will live within a container, the `App` container. Go ahead and add a file and use the following starter code; it's the same starter code for other sagas.

######app/containers/App/sagas.js
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

###### app/containers/App/sagas.js

```JS
import { take, call, put, select } from 'redux-saga/effects';

// Your sagas for this container
export default [
sagaName,
];

// Individual exports for testing
export function* sagaName() {

}
```

Next we'll import the `sagas.js` file into our `App` route configuration by updating our `getComponent` function to asynchronosly load our files. You can just replace it with the one below.
######app/routes.js
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

###### app/routes.js

```JS
export default function createRoutes(store) {
// create reusable async injectors using getAsyncInjectors factory
const { injectReducer, injectSagas } = getAsyncInjectors(store);

childRoutes : [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

childRoutes = [

// All your child routes are here inside this array
];

return {
getComponent(nextState, cb) {
const importModules = Promise.all([
import('containers/App/sagas'),
import('containers/App'),
]);

const renderRoute = loadModule(cb);

importModules.then(([sagas, component]) => {
injectSagas(sagas.default);
renderRoute(component);
});

importModules.catch(errorLoading);
},
childRoutes,
};
}
```