Is there a way to use react-snap with React.lazy directly?
loadable-component has evolved and focused on server side rendering.
import { hydrate, render } from 'react-dom';
import { loadableReady } from '@loadable/component';
// ⚠️not more getState() in newer loadable-components so no more: window.snapSaveState = () => getState();
if (bootstrapedElement.hasChildNodes()) {
loadableReady().then(() => {
return hydrate(<Application />, bootstrapedElement);
});
}
return render(<Application />, bootstrapedElement);
index.js
const rootElement = document.getElementById("root");
const root = ReactDom.unstable_createRoot(rootElement);
root.render(<App />);
App.js
import React, { Component, Suspense } from "react";
const Comp = React.lazy(() => import('./Comp'));
class App extends Component {
render() {
return (
<Suspense fallback={<div>Loading...</div>} maxDuration={5000}>
<Comp/>
</Suspense>
);
}
}
Something like this
Thank you @stereobooster
But I have no success right now...
I guess I need to find a trick for filling window.snapSaveState object depending all React.lazy component I set?
As far as I understood react-snap will then add <script src="some_async.js" async></script> tags?
Can you post reproducible example? Posted example should work. It suppose to wait 5 seconds before flushing DOM. Are you sure your issue is with async components and not something else?
Sorry I was inaccurate with components. I use React.lazy for my react-router (v4+) pages.
I'm upgrading an open source boilerplate, so I will soon push my upgrading branch if I still struggle.
It may be more comfortable to talk about then.
Alright, I got it (my boilerplate in master branch).
I had several issues but most tricky one was due to react-router waiting for a component but page was React.lazy imported and router threw something like prop error: receiving object but waiting a function....
Solution is just to wrap all pages with a <Suspense /> — with fallback — (react-router see a component even if page is not loaded):
Then react-snap job is pretty standard:
import { hydrate, render } from 'react-dom';
const ELEMENT_TO_BOOTSTRAP = 'root';
const bootstrapedElement = document.getElementById(ELEMENT_TO_BOOTSTRAP);
// #region render (with hot reload if dev)
const renderApp = RootComponent => {
const Application = () => (
<AppContainer>
<RootComponent />
</AppContainer>
);
// needed for react-snap:
if (bootstrapedElement.hasChildNodes()) {
return hydrate(<Application />, bootstrapedElement);
}
return render(<Application />, bootstrapedElement);
};
renderApp(Root);
But obviously the first render gives a little flickering (as reason explained in README).
I'd like to set manually window.snapSaveState (I know which page is dynamically imported so I could prepare the state?)
Suspense doesn't work without async rendering e.g.
const root = ReactDom.unstable_createRoot(rootElement);
at least this is based on my experiments, you can double check in React documentation or ask React team about it
Yes just hoped to find a trick.
Anyway thank you @stereobooster .
I close this no more issue (is wasn't actually).
Hi there,
Is there a reason why I cannot find unstable_createRoot on ReactDOM. I'm using version 16.6.3 (last stable).
Because it is added in React 16.6
I'm actually using react and react dom 16.6.3
here is working example https://github.com/stereobooster/react-lingui-example/blob/suspense/src/index.js
Resolution: add to documentation which version of loadable-components works until react will fix concurrent mode. At the moment I use "loadable-components": "^2.2.3" (which were deprecated 4 months ago)
Still any other solution to run React-snap with async loading ? (With react lazy or loadable-components new versions ?)