React-snap: Any guidance for React.lazy (or at least a solution for breaking changed @loadable/component)?

Created on 20 Nov 2018  ·  14Comments  ·  Source: stereobooster/react-snap

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);

This does not work anymore!

bug enhancement help wanted question

All 14 comments

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

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 ?)

Was this page helpful?
0 / 5 - 0 ratings