I have an isomorphic (aka universal) React app that I'm working on mostly to learn.
The backend is a separate project that both the server and client makes requests to. So it's not the same server that renders the React app.
Server rendering is set up like this, a bit simplified:
const
data = {
body: '',
initialState: {}
},
store = configureStore(); // Middleware and so on
match({location, routes}, (err, redirectLocation, renderProps) => {
// Handle error
// Handle redirect
fetchData(store.dispatch, renderProps.components, renderProps.params)
.then(() => {
data.body = renderToString({
<Provider store={store}>
<RouterContext {...renderProps} />
</Provider>
});
data.initialState = store.getState(); // dehydrate
return renderToStaticMarkup(<HtmlComponent {...data} />);
})
.then((html) => res.status(200).send('<!doctype html>' + html))
.catch((err) => res.status(500).send(err.stack));
});
The method fetchData()
looks like this:
export function fetchData(dispatch, components, params) {
const needs = components.reduce((prev, current) => {
if(!current) return [];
return Object.keys(current).reduce((acc, key) => {
return current[key].hasOwnProperty('needs') ? current[key].needs.concat(acc) : acc;
}, prev);
}, []),
promises = needs.map((need) => dispatch(need(params)));
return Promise.all(promises);
}
A connected React component is set up like this:
const Component = React.createClass({
statics: {
needs: [
Actions.getHistory
]
},
componentWillMount: function() {
const needs = this.constructor.needs;
for(let i = 0, len = needs.length; i < len; i++) {
this.props.dispatch(needs[i]());
}
},
// Code
};
On the server the needs
actions for a route is completed using fetchData()
before the react app is executed. In the client I want the needs
actions to be dispatched on componentWillMount()
, but only if it's not the initial route. On the initial route the actions are already completed on the server and the store rehydrated in the client.
The problem is that I don't know how to avoid dispatching actions in componentWillMount()
on the server and on the initial route in the client. Which means that when the page is server rendered the actions are dispatched thrice.
I've set up the project to learn how to write isomorphic apps. This setup is something I think is pretty common and I've seen used a lot around the internet. But I've not found a way to prevent the double/triple action dispatches.
The only thing I can come up with is a global state like isServerRender = true/false
and isRehydratedStateFresh = true/false
. But I would really like to avoid global states.
Is there a conventional way to solve this? Or a solution that doesn't require global states?