I am trying to refactor a React 18 app to use lazy loading as described in the docs:
The best way to introduce code-splitting into your app is through the dynamic import() syntax.
My original code (working):
import addMobileAppStartupListeners from './components/listeners/addMobileAppStartupListeners';
const startApp = () => {
if (isPlatformMobile()) {
addMobileAppStartupListeners();
}
startAccessibilityTestingIfDebug();
};
addMobileAppStartupListeners.tsx
const addMobileAppStartupListeners = () => {
document.addEventListener(
'deviceready',
() => {
// Method called when tapping on a notification
PluginPushNotifactions.addListener(
'pushNotificationActionPerformed',
(notification: PluginActionPerformed) => {
debugLog('push registration tapped', notification);
},
);
},
false,
);
};
export default addMobileAppStartupListeners;
Since this is a default export I thought I could use import:
const startApp = () => {
if (isPlatformMobile()) {
import('./components/listeners/addMobileAppStartupListeners').then(
(addMobileAppStartupListeners) => {
addMobileAppStartupListeners();
},
);
}
startAccessibilityTestingIfDebug();
};
But TypeScript complains:
[react-scripts] ERROR in src/index.tsx:41:9
[react-scripts] TS2349: This expression is not callable.
[react-scripts] Type 'typeof import("/Users/private/d/ionic/src/components/listeners/addMobileAppStartupListeners")' has no call signatures.
I don't know how to fix this. My knowledge of TypeScript is pretty rudimentary. I think I need to give it the type of the function returned by the import, but I don't know how to do that.
** Environment details***
Typescript 4.9.5
{
"compilerOptions": {
"target": "es2017",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"noFallthroughCasesInSwitch": true
},
// Config files.
"files": [
"capacitor.config.ts",
"ionic.config.json",
"lingui.config.ts",
],
"include": [
"src",
"tests/playwright",
],
}
What may not be obvious from the docs example, is that the result of the dynamic import is the module itself:
Returns a promise which fulfills to a module namespace object: an object containing all exports from
moduleName
.
So if your module has a default export, make sure to use the .default
property to get your actual exported function:
import('./components/listeners/addMobileAppStartupListeners').then(
// Result is the module containing all its exports, including default
(addMobileAppStartupListeners) => {
// Here you want the default exported function
addMobileAppStartupListeners.default();
}
);