So after digging into this over the weekend, it looks like React Loadable has made too many assumptions about the way it’ll be used with WebPack to be used for Meteor’s SSR easily. So we always get a warning about the hydrated HTML not matching.
The problem is basically that Meteor’s import method is always run asynchronously, even if you import a module which has been previously loaded. \
The load
method inside React Loadable seems to suggest that in WebPack’s dynamic import method, it’ll run synchronously if the module has been preloaded - I’m not certain of this.
// From React Loadable
function load (loader) {
var promise = loader();
var state = {
loading: true,
loaded: null,
error: null
};
// I'm guessing this runs synchronously in WebPack,
// and that's how it knows to not show loading?
// This doesn't work to avoid rendering loading in Meteor.
state.promise = promise.then(function (loaded) {
state.loading = false;
state.loaded = loaded;
return loaded;
}).catch(function (err) {
state.loading = false;
state.error = err;
throw err;
});
return state;
}
I wonder if Meteor should be running already loaded import promises synchronously?
So you can preload all the modules manually, but when you call Loadable’s preloadReady
method, it doesn’t really know which Loadables to wait for. Preloading the modules before hydration might make this perceptually irrelevant though (I don’t see a flash of “Loading” when I preload the modules) … I’m not sure.
I also can only get this to work for rooted import paths - I wonder whether Loadable’s babel plugin can be modified to include the current path, or always convert the path to rooted paths, so that preloading the modules always works. I also wonder if there is a simpler Meteor tool to get that to work (Meteor’s isobuild system has been harder to figure out that WebPack’s, and I’m not sure where to ask questions about it).
So anyway, if we are going to make this work in Meteor, we need a new preloadReady
method which can take a list of modules, load them, then avoid showing the Loading component if they have already been loaded. A synchronous way to import dynamic modules would be the easiest way to do this, but maybe we can use a registry of promises. We’d still use the babel plugin which gets us the module path outside of the loader method, and instead of just running the loader as Loadable does now, we’d look at the modulesName, and see if we have a preloaded promise matching that path. If we have an extant promise, we just use that, and resolve immediately.
Actually, that wouldn’t even add too much cruft on top of what we already have in React Loadable. I wonder if Jamie (maintainer of React Loadable) would accept a PR with that in it.