I'm building an universal app with React
and Redux
which renders on server-side via NodeJS
and ExpressJS
.
all is working fine, Express handlers call {match} from 'react-router'
and a new store instance
will be created each time.
The issue I'm having is: the {renderToString} from 'react-dom/server'
renders only the pristine
version of the store, if something changes the store (eg: action dispatched via componentWillMount
) the store will be updated but the generated markup
will not change until a new renderToString
is called.
- I don't know how (per-request) the reducers will change the state, so, I can't provide an initial state before calling
renderToString
. - I would like to avoid a further
renderToString
call.
this is my sample code:
const store = createStore(
reducers,
// an object that provides the initial generic state
res.viewModel.__INITIAL_STATE__ || Object.create(null),
middlewares
);
// now the store is pristine and calling store.getState()
// I will retrieve an invalid version of the state
const markup = renderToString(
<Provider store={store}>
{<RouterContext {...renderProps} />}
</Provider>
);
// now the store is correctly computed and calling
// store.getState() gets the right version but the markup
// generated is still old. Only recalling `renderToString` will get
// the right markup
const markup2 = renderToString(
<Provider store={store}>
{<RouterContext {...renderProps} />}
</Provider>
);
One approach is to provide a per-route action as a static method on your Route components which would then be called by your express middleware before calling
renderToString
e.g.Then in your middleware you'd call all the static
fetchData
methods defined on the matched route components before rendering, e.g.You can see a complete example here in the 60frames boilerplate.
Relevant modules:
https://github.com/60frames/react-boilerplate/blob/master/src/components/index/Index.js
https://github.com/60frames/react-boilerplate/blob/master/src/server.js