diff --git a/extensions/roc-package-web-app-react-dev/src/config/roc.config.js b/extensions/roc-package-web-app-react-dev/src/config/roc.config.js index b5a7cb2..603373e 100644 --- a/extensions/roc-package-web-app-react-dev/src/config/roc.config.js +++ b/extensions/roc-package-web-app-react-dev/src/config/roc.config.js @@ -7,6 +7,7 @@ export default { useDefaultReducers: true, middlewares: 'src/redux/middlewares.js', useDefaultMiddlewares: true, + sagas: 'src/redux/sagas.js', }, // Will be moved to redux above eventually, now kept here to make the template upgradable diff --git a/extensions/roc-package-web-app-react-dev/src/config/roc.config.meta.js b/extensions/roc-package-web-app-react-dev/src/config/roc.config.meta.js index 8ccedcd..1688e23 100644 --- a/extensions/roc-package-web-app-react-dev/src/config/roc.config.meta.js +++ b/extensions/roc-package-web-app-react-dev/src/config/roc.config.meta.js @@ -45,6 +45,10 @@ export default { ' documentation for what middlewares that are included.', validator: required(isBoolean), }, + sagas: { + description: 'The Redux Saga to use as the root saga.', + validator: notEmpty(isPath), + }, }, clientLoading: { description: 'The React component to use on the first client load while fetching data, will only ' + diff --git a/extensions/roc-package-web-app-react-dev/src/webpack/index.js b/extensions/roc-package-web-app-react-dev/src/webpack/index.js index c058767..0ee28a7 100644 --- a/extensions/roc-package-web-app-react-dev/src/webpack/index.js +++ b/extensions/roc-package-web-app-react-dev/src/webpack/index.js @@ -47,6 +47,17 @@ export default ({ ); } + const hasSagas = !!(buildSettings.redux.sagas && fileExists(buildSettings.redux.sagas)); + if (hasSagas) { + const sagas = getAbsolutePath(buildSettings.redux.sagas); + + newWebpackConfig.plugins.push( + new webpack.DefinePlugin({ + REDUX_SAGAS: JSON.stringify(sagas), + }) + ); + } + const hasClientLoading = !!(buildSettings.clientLoading && fileExists(buildSettings.clientLoading)); if (hasClientLoading) { const clientLoading = getAbsolutePath(buildSettings.clientLoading); @@ -77,6 +88,7 @@ export default ({ HAS_REDUX_REDUCERS: hasReducers, HAS_REDUX_MIDDLEWARES: hasMiddlewares, + HAS_REDUX_SAGA: hasSagas, HAS_CLIENT_LOADING: hasClientLoading, HAS_TEMPLATE_VALUES: hasTemplateValues, }) diff --git a/extensions/roc-package-web-app-react/app/client/create-client.js b/extensions/roc-package-web-app-react/app/client/create-client.js index 1813e8a..fc0b518 100644 --- a/extensions/roc-package-web-app-react/app/client/create-client.js +++ b/extensions/roc-package-web-app-react/app/client/create-client.js @@ -1,4 +1,5 @@ -/* global __DEV__, HAS_CLIENT_LOADING, ROC_CLIENT_LOADING, ROC_PATH, HAS_REDUX_REDUCERS, document, window */ +/* global __DEV__, HAS_CLIENT_LOADING, ROC_CLIENT_LOADING, ROC_PATH, HAS_REDUX_REDUCERS, document, window, + HAS_REDUX_SAGA, REDUX_SAGAS */ /* eslint-disable global-require */ import React from 'react'; import ReactDOM from 'react-dom'; @@ -94,6 +95,11 @@ export default function createClient({ createRoutes, createStore, mountNode }) { const { syncHistoryWithStore } = require('react-router-redux'); const store = createStore(history, window.FLUX_STATE); + + if (HAS_REDUX_SAGA) { + store.runSaga(require(REDUX_SAGAS).default); + } + history = syncHistoryWithStore(history, store, { // We do not want to use adjustUrlOnReplay if the browser does // not support the history API with pushState since this can lead diff --git a/extensions/roc-package-web-app-react/app/server/useReact.js b/extensions/roc-package-web-app-react/app/server/useReact.js index 3c49cad..2af0105 100644 --- a/extensions/roc-package-web-app-react/app/server/useReact.js +++ b/extensions/roc-package-web-app-react/app/server/useReact.js @@ -1,4 +1,4 @@ -/* global __DIST__, __DEV__ HAS_TEMPLATE_VALUES, TEMPLATE_VALUES, ROC_PATH */ +/* global __DIST__, __DEV__ HAS_TEMPLATE_VALUES, TEMPLATE_VALUES, ROC_PATH, HAS_REDUX_SAGA, REDUX_SAGAS */ import useReactLib from 'roc-package-web-app-react/lib/app/server/useReact'; @@ -7,6 +7,12 @@ import Header from '../shared/header'; export default function useReact(createServer) { // eslint-disable-next-line const templateValues = HAS_TEMPLATE_VALUES ? require(TEMPLATE_VALUES) : undefined; + let reduxSagas; + + if (HAS_REDUX_SAGA) { + reduxSagas = require(REDUX_SAGAS).default; + } + return useReactLib(createServer, { dev: __DEV__, dist: __DIST__, @@ -14,5 +20,6 @@ export default function useReact(createServer) { templateValues, rocPath: ROC_PATH, Header, + reduxSagas, }); } diff --git a/extensions/roc-package-web-app-react/app/shared/flux/create-store.js b/extensions/roc-package-web-app-react/app/shared/flux/create-store.js index 490f62a..6ebe25a 100755 --- a/extensions/roc-package-web-app-react/app/shared/flux/create-store.js +++ b/extensions/roc-package-web-app-react/app/shared/flux/create-store.js @@ -1,4 +1,4 @@ -/* globals __DEV__, __WEB__, window */ +/* globals __DEV__, __WEB__, window, HAS_REDUX_SAGA */ import { createStore, applyMiddleware, combineReducers, compose } from 'redux'; import { routerMiddleware, routerReducer } from 'react-router-redux'; @@ -20,6 +20,14 @@ export default function createReduxStore(reducers, ...middlewares) { (history, initialState) => { let finalCreateStore; const normalMiddlewares = [].concat(middlewares); + let sagaMiddleware; + + // redux-saga + if (HAS_REDUX_SAGA) { + const createSagaMiddleware = require('redux-saga').default; // eslint-disable-line + sagaMiddleware = createSagaMiddleware(); + normalMiddlewares.push(sagaMiddleware); + } // Add the react-router-redux middleware normalMiddlewares.push(routerMiddleware(history)); @@ -68,6 +76,10 @@ export default function createReduxStore(reducers, ...middlewares) { }); } + if (sagaMiddleware) { + store.runSaga = sagaMiddleware.run; + } + return store; }; } diff --git a/extensions/roc-package-web-app-react/package.json b/extensions/roc-package-web-app-react/package.json index 675c3e5..cde2281 100644 --- a/extensions/roc-package-web-app-react/package.json +++ b/extensions/roc-package-web-app-react/package.json @@ -42,6 +42,7 @@ "react-server-status": "~1.0.0", "redial": "~0.4.1", "redux": "~3.4.0", + "redux-saga": "0.12.0", "redux-thunk": "~2.1.0", "roc": "^1.0.0-rc.12", "roc-package-web-app": "^1.0.0-beta.3", diff --git a/extensions/roc-package-web-app-react/src/app/server/reactRenderer.js b/extensions/roc-package-web-app-react/src/app/server/reactRenderer.js index f0ea16b..a52e036 100755 --- a/extensions/roc-package-web-app-react/src/app/server/reactRenderer.js +++ b/extensions/roc-package-web-app-react/src/app/server/reactRenderer.js @@ -79,6 +79,7 @@ export function reactRender({ staticRender = false, hasTemplateValues, templateValues, + reduxSagas, }) { return new Promise((resolve) => { match({ history, routes: createRoutes(store), location: url }, @@ -109,10 +110,21 @@ export function reactRender({ const hooks = rocConfig.runtime.fetch.server; + let sagaPromise; + if (reduxSagas) { + sagaPromise = store.runSaga(reduxSagas).done; + } + return triggerHooks({ renderProps, hooks, locals, + }).then((result) => { + if (sagaPromise) { + store.dispatch(require('redux-saga').END); // eslint-disable-line + return sagaPromise.then(() => result); + } + return result; }).then(({ redialMap, redialProps }) => { let component = ; diff --git a/extensions/roc-package-web-app-react/src/app/server/reactRouter.js b/extensions/roc-package-web-app-react/src/app/server/reactRouter.js index 74e341e..d476d67 100755 --- a/extensions/roc-package-web-app-react/src/app/server/reactRouter.js +++ b/extensions/roc-package-web-app-react/src/app/server/reactRouter.js @@ -18,6 +18,7 @@ export default function reactRouter({ templateValues, rocPath, Header, + reduxSagas, }) { const rocConfig = getSettings(); @@ -70,6 +71,7 @@ export default function reactRouter({ koaState: this.state, hasTemplateValues, templateValues, + reduxSagas, }); if (redirect) { diff --git a/extensions/roc-package-web-app-react/src/roc/index.js b/extensions/roc-package-web-app-react/src/roc/index.js index 57c8bd3..4c7d39b 100644 --- a/extensions/roc-package-web-app-react/src/roc/index.js +++ b/extensions/roc-package-web-app-react/src/roc/index.js @@ -23,7 +23,7 @@ export default { 'react-router-redux', 'react-server-status', 'redial', - + 'redux-saga', 'react-router-redial', 'redux', 'redux-thunk',