Reputation: 827
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>; }>'.
Upvotes: 5
Views: 3045
Reputation: 1266
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;
}
}
Upvotes: 0
Reputation: 37327
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>
);
}
Upvotes: 2
Reputation: 480
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.
Upvotes: 3