Trouble with server-side rendering
See original GitHub issueMy current saga setup works flawlessly on the client side but does not work at all on the server side 😕 . My current shared/redux/configureStore.js
module is:
import { createStore, applyMiddleware, compose } from 'redux';
import { combineReducers } from 'redux-immutable';
import { fromJS } from 'immutable';
import { LOCATION_CHANGE } from 'react-router-redux';
import createSagaMiddleware, { END } from 'redux-saga';
import reduxCatch from 'redux-catch';
import postReducer from './modules/post';
function errorHandler(error, getState) {
console.error(error);
console.debug('current state', getState());
}
function routeReducer(state = fromJS({ locationBeforeTransitions: null }), action) {
if (action.type === LOCATION_CHANGE) {
return state.merge({
locationBeforeTransitions: action.payload,
});
}
return state;
}
export default function configureStore(initialState) {
const sagaMiddleware = createSagaMiddleware();
const store = createStore(combineReducers({ postReducer, route: routeReducer }), initialState, compose(
applyMiddleware(sagaMiddleware, reduxCatch(errorHandler)),
typeof window === 'object' && typeof window.devToolsExtension !== 'undefined' ? window.devToolsExtension() : f => f,
));
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('./modules/post', () => {
const nextReducer = require('./modules/post'); //eslint-disable-line
store.replaceReducer(nextReducer);
});
}
store.runSaga = sagaMiddleware.run; //eslint-disable-line
store.close = () => store.dispatch(END); //eslint-disable-line
return store;
}
and the section of the server/server.js
that handles server-side rendering (which I’ve tried several approaches with in terms of running my “rootPostSaga” from shared/redux/modules/post.js
) is:
// Server Side Rendering based on routes matched by React-router.
app.use((req, res) => {
match({ routes, location: req.url }, (err, redirectLocation, renderProps) => {
if (err) {
return res.status(500).end('Internal server error');
}
if (!renderProps) {
return res.status(404).end('Not found!');
}
const initialState = fromJS({
postReducer: {
posts: [],
post: {},
},
route: {
locationBeforeTransitions: null,
},
});
const store = configureStore(initialState);
store.runSaga(rootPostSaga).done.then(() => {
fetchComponentData(store.dispatch, renderProps.components, renderProps.params)
.then(() => {
const createElement = (Component, props) => (
<Component
{...props}
radiumConfig={{ userAgent: req.headers['user-agent'] }}
/>
);
const initialView = renderToString(
<Provider store={store}>
<RouterContext {...renderProps} createElement={createElement} />
</Provider>
);
const finalState = store.getState().toJS();
res.status(200).end(renderFullPage(initialView, finalState, getFilename()));
})
.catch((error) => {
res.end(renderFullPage(`Error: ${error}`, {}, getFilename()));
});
});
store.close();
});
});
where “fetchComponentData” comes from:
/*
* This was inspired from
* https://github.com/caljrimmer/isomorphic-redux-app/blob/73e6e7d43ccd41e2eb557a70be79cebc494ee54b/src/common/api
* /fetchComponentDataBeforeRender.js
*/
import Promise from 'bluebird';
export function fetchComponentData(dispatch, components, params) {
const needs = components.reduce((prev, current) => {
return (current.need || [])
.concat((current.WrappedComponent ? current.WrappedComponent.need : []) || [])
.concat(prev);
}, []);
const promises = needs.map(need => dispatch(need(params)));
return Promise.all(promises);
}
It appears as though the “rootPostSaga” is not run at all when preparing the page to be rendered server-side.
Issue Analytics
- State:
- Created 7 years ago
- Comments:19 (13 by maintainers)
Top Results From Across the Web
Challenges in server side rendering React apps (SSR)
Data Hydration is a typical problem we encounter with server side rendering. The server has pre-fetched data. However, when we call ReactDOM.
Read more >What is server-side rendering: definition, benefits and risks
Server-Side Rendering (SSR) is an approach to rendering content for the website. ... There are fewer issues with social media indexing.
Read more >Why Server-Side Rendering Alone Is Not the Solution
The problem is, because the server assembles the UI, the UI, the server, and the data(base) need to be near each other. Otherwise,...
Read more >What is server-side rendering and how does it improve site ...
Server-side rendering ensures that website content appears quickly, without first having to download and run application code.
Read more >The Challenges and Pitfalls of Server Side Rendering
Server - Side Rendering (SSR) has become a go-to method for improving the load time of dynamic websites. Rendering content on the server...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
As @ojkelly said you don’t need both
fetchComponentData
and the two-phase render on the server.componentWillMount
to dispatch data loading actions (componentDidMount
will not work on the server) then you need to follow this steps:componentWillMount
and thus will dispatch the load actions for Sagas. You need to close the store immediately afterdone
promise of the root saga will resolve/reject. At this point the Store’s state is already updated by Sagas and you can do your 2nd render that will be sent to the client@ojkelly a small correction, the 2nd render is the one into
done
callback@ojkelly I think
store.close()
should come after the 1st renderToString so that components can dispatch their actions insidecomponentWillMount