Awaiting a dynamic import
Asked Answered
R

4

7

I've created a web application with React and Typescript, using create-react-app. It uses a rather heavy third party library. I want to exclude that from the main bundle by using dynamic import expressions.

So, instead of doing import { Component } from 'library', I've created a little wrapper that looks like this:

const loadLibrary = async () => {
    const x = await import('library').then((r) => r);
    return x;
};
const {
    Component,
} = loadLibrary() as any;

// tslint:disable-next-line:no-console
console.log(`typeof Component is ${typeof Component}`);

export {
    Component,
};

And then, in my app, I'd use import { Component } from '../library-wrapper.ts'. Because the wrapper uses dynamic import expressions, Webpack creates a separate chunk which is only downloaded by the browser then it is actually needed. \o/.

But the bad news is: my wrapper doesn't actually await the dynamic import expression. It invokes the loadLibrary() function but immediately continues execution, logging

typeof Component is undefined

So when I attempt to use the Component, React crashes:

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.

Any suggestions what is going on?

Rockett answered 7/4, 2018 at 10:37 Comment(0)
S
9

loadLibrary is an asynchronous function, so it returns a promise instead of a regular object. Therefore you have to do await loadLibrary() or loadLibrary().then(...) to get the library object.

A consequence of this is that you can't statically export something that's imported dynamically, because static imports/export are done immediately and synchronously while dynamic imports are done asynchronously. You can only export the function loadLibrary, and let users of the module call that when they need the library.

In short, once asynchronous, always asynchronous; you can't force something asynchronous to run synchronously in JavaScript.


Also, as an aside, your loadLibrary function could be simplied a lot, to just

const loadLibrary = () => import('library');

since a) .then((r) => r) just creates an identical copy of a promise, b) you don't have to await before returning in an async function (it's done automatically) and c) a function that returns a promise anyways doesn't have to be marked as async (although you still can if you wish, for readability for example).

Secunda answered 7/4, 2018 at 10:47 Comment(2)
I thought the whole idea of async/await is that you could actually wait for a Promise to be resolved. Have I been wrong in this case?Rockett
Yes, you're right, but you still have to use the await keyword to wait for a promise (to tell JS that it's the result of a promise you want instead of the promise itself). And because you can't run asynchronous code in a synchronous way (which I believe is due to the single-treaded design of JavaScript), you can only use await inside a function marked as async, otherwise you would have to use .then(r => { /* do something with result */ }).Secunda
C
1

I made an IIFE that receives a prop named pdfName:

const Component = ({ pdfName }) => {
const [pdf, setPdf] = useState(undefined);

(async () => {
    if (pdfName !== '') {
      await import(`../../assets/docs/${pdfName}.pdf`).then((r) => {
        setPdf(r.default);
      });
    }
  })();
  ...
Coucher answered 20/10, 2021 at 5:33 Comment(0)
H
0

In my case, application crashed with undefined "Popup", "Confirm" constants. Loading it using React Lazy - solved this problem !

const Popup = lazy(() => import("./Popup"));
const Confirm = lazy(() => import("./Confirm"));

enter image description here

Hypochondria answered 28/12, 2021 at 17:12 Comment(0)
U
0

I might be late to answer this question but you can use React.lazy to load the module dynamically

const MCard = React.lazy(() => import("module_name_to_import"))

Also there is a JS function import() which can be use to import the module dynamically. this function always return a promise.

import("module_name_to_import").then((mod) =>{//Your code will come here})

Hopefully this answers your question.

Uzial answered 15/5 at 11:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.