2

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",
  ],
}
2
  • I guess this can be a problem with tsconfig/typescript version. Dynamic imports are available from ts >= 2.4. Also check "module" field in tsconfig. Commented Feb 27, 2023 at 10:57
  • @АлексейМартинкевич I'm using TypeScript 4.9.5. I updated the question with my tsconfig. Commented Feb 27, 2023 at 11:36

1 Answer 1

2

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();
  }
);
Sign up to request clarification or add additional context in comments.

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.