use of React.Lazy for services (non-component) type of code

Viewed 820

Per documentation, React.Lazy needs to be wrapped in a <suspense>. Can I use React.Lazy to lazily load a service which exports a function?

    const actionsList = React.lazy(() => import('@pkg/actions-list-service'));

and the action-List-service exports a function in a .ts file. What does React recommended way for lazily loading non-component type of code? This is the error I get trying to lazily load the service code I got this error:

Type 'Promise<{ default: typeof import("D:/services/actions-list-service/dist/types"); useActionsList: () => (itemElement: HTMLElement | null | undefined) => readonly ActionsListItem[]; }>' is not assignable to type 'Promise<{ default: ComponentType<any>; }>'.
2 Answers

If you check the source of React.lazy(), you'll see that it's geared towards component rendering, as you could guess. In order to lazy load something, you can just use async import:

// example code, to show possible solution

const moduleMap = {
  module1: () => import('./module1.js'),
  module2: () => import('./module2.js')
}

function doSomething(moduleName) {
  // You might call this one in useEffect() for example
  const module = moduleMap[moduleName]
  if (module) {
    module().then(({default: actualModule, member1}) => {
      // use the module members
    })
  }
}

That will allow lazy loading and possibly code splitting as well.

If we want to trigger React Suspense loader, then we have to provide a component that will load the independent library.

For example I want to lazy load the html5-qrcode Html5Qrcode module that it's 314K (gzipped 93.5K).

We create a loader component that will have an onLoaded callback prop and it just returns the module like:

import { useEffect } from 'react';
import { Html5Qrcode } from 'html5-qrcode/esm/html5-qrcode';

export default function Html5QrcodeLoader({ onLoaded }) {
  useEffect(() => {
    onLoaded(Html5Qrcode);
  }, []);
  return null;
}

Then we import that component using React.lazy like:

const Html5QrcodeLoader = React.lazy(() =>
  import(
    '@components/Html5QrcodeLoader' /* webpackChunkName: "Html5QrcodeLoader" */
  )
);

And now we can use the loader to our component like this:

export default function QrCodeScanner() {
  const [Html5Qrcode, setHtml5Qrcode] = useState();

  useEffect(() => {
    if (!Html5Qrcode) {
      return;
    }

    // Use lazy loaded module Html5Qrcode
  }, [Html5Qrcode]);

  const handleHtml5QrcodeLoaded = module => {
    // Careful, always do it using a function,
    // else the module itself will be initialized!
    setHtml5Qrcode(() => module);
  };

  if (!Html5Qrcode) {
    // Lazy load it
    return <Html5QrcodeLoader onLoaded={handleHtml5QrcodeLoaded} />;
  }

  log('Html5Qrcode loaded');

  // Now render

  return (
    <div>
      <div>QrCodeScanner</div>
    </div>
  );
}
Related