5

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>; }>'.

3 Answers 3

3

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.

Sign up to request clarification or add additional context in comments.

Comments

2

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

Comments

0

My solution for dynamically importing google-libphonenumber (a heavy library that must be lazily loaded)

phoneutil.ts

/** Dynamic import for code splitting/lazy loading. */
const googleLibphoneNumberModule = () => import('google-libphonenumber');

let phoneUtil: libphonenumber.PhoneNumberUtil;
const modulePromise = googleLibphoneNumberModule().then(({ default: { PhoneNumberUtil } }) => {
  phoneUtil = PhoneNumberUtil.getInstance();
});

export async function isPhoneNumberValid(phone: string) {
  try {
    await modulePromise;
    return phoneUtil.isValidNumber(phoneUtil.parseAndKeepRawInput(phone));
  } catch (error) {
    return false;
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.