The application I'm working on is based on React Fiber and React Router V3.
Trying to use hydrate()
instead of render()
with async components I've faced with the following issue: HTML returned from SSR is different from client-side one.
As a result React remounts the whole DOM and throws the following warning: Did not expect server HTML to contain...
.
React Training does not provide solution as well: Code-splitting + server rendering
Is there any solution to achieve this?
(pseudo code)
App.js:
export default () => <div>Lorem Ipsum</div>;
client.js:
const createRoutes = store => ({
path: '/',
getComponent(nextState, cb) {
require('./App'); // some async require
},
onEnter: (nextState, replace, cb) => {
store.dispatch(fetchData())
.then(() => cb())
.catch(cb);
}
});
match({history, routes: createRoutes(store)},
(error, redirectLocation, renderProps) => {
hydrate(
<Router history={history} routes={createRoutes(store)} />,
document.getElementById('app')
);
});
server.js
match({routes: createRoutes(store), location: req.url},
(err, redirectLocation, renderProps) => {
const content = renderToString(<RouterContext {...renderProps}/>);
// send content to client
});
I've been investigated the issue a bit more deeper and found the solution. To achieve DOM hydration the following point should be token in account:
I the example above in client.js
I invoked createRoutes(store)
twice. This is redundant because renderProps
already has routes
property prepared for <Route />
component. Due to this mistake onEnter
was called twice, so data fetching was performed twice too.
To avoid HTML mismatch on server and client side data fetching in onEnter
should not be called on the first client-side render.
match
function waits for getComponent
callback is performed before render. So the main question is wrong, because this functionality is available out of the box.