paypal / react-engine

a composite render engine for universal (isomorphic) express apps to render both plain react views and react-router views
Apache License 2.0
1.45k stars 130 forks source link

Client-side error when using code splitting in webpack #128

Closed pklicnik closed 8 years ago

pklicnik commented 8 years ago

Our routes file is configured to use require.ensure so we can code-split our page into separate chunks using webpack. Eg:

if (typeof require.ensure === "undefined") {
    require.ensure = require("node-ensure");
}

var Page = require("page.jsx");

module.exports = {
    component: Page,
    path: "/home",
    childRoutes: [
        {
            path: ":id",
            getComponent: function(location, callback) {
                require.ensure([], function() {
                    callback(null, require("home.jsx"));
                });
            }
        }
    ]
};

Server-side rendering succeeds without error, but an error appears in the console when mounted on the client:

Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
 (client) <noscript data-reacti
 (server) <div data-reactid=".1

After debugging through the issue, this seems to be a problem/bug/limitation in react-router.

As a work around, you need to wrap the call to render inside match to pre-load the routes configuration on the client. Snippet of the change needed on the react-engine side

match({ routes: options.routes, location: location }, function() {
  // for any component created by react-router, merge model data with the routerProps
  // NOTE: This may be imposing too large of an opinion?
  var routerComponent = React.createElement(RouterComponent, {
    createElement: function(Component, routerProps) {
      return React.createElement(Component, merge({}, props, routerProps));
    },

    routes: options.routes,
    history: routerHistory.createHistory()
  });

  render(routerComponent, mountNode);
});

Solution is discussed here: https://github.com/rackt/react-router/issues/2036#issuecomment-153541487

Example provided here: https://github.com/rackt/example-react-router-server-rendering-lazy-routes

samsel commented 8 years ago

@pklicnik is the change needed on the server or client side of react-engine?

pklicnik commented 8 years ago

@samsel Client side.

See PR #129 for my workaround. Note: I didn't fully test all scenarios, so tweak as needed

dglozic commented 8 years ago

+1

samsel commented 8 years ago

merged & published v3.1.0 with the change!