ES6: Conditional & Dynamic Import Statements

JavascriptEcmascript 6

Javascript Problem Overview


Conditional

Is it possible to have conditional import statements like below?

if (foo === bar) {
    import Baz from './Baz';
}

I have tried the above but get the following error (from Babel) when compiling.

'import' and 'export' may only appear at the top level

Dynamic

Is it possible to have dynamic import statements like below?

for (let foo in bar) {
    if (bar.hasOwnProperty(foo)) {
        import Baz from `./${foo}`;
    }
}

The above receives the same error from Babel whilst compiling.

Is this possible to do or is there something I am missing?

Reasoning

The reason I am trying to do this is that I have a lot of imports for a number of "pages" and they follow a similar pattern. I would like to clean up my code base by importing these files with a dynamic for loop.

If this is not possible then is there a better way to handle large number of imports in ES6?

Javascript Solutions


Solution 1 - Javascript

We do have dynamic imports proposal now with ECMA. This is in stage 2. This is also available as babel-preset.

Following is way to do conditional rendering as per your case.

if (foo === bar) {
    import('./Baz')
    .then((Baz) => {
       console.log(Baz.Baz);
    });
}

This basically returns a promise. Resolution of promise is expected to have the module. The proposal also has things like multiple dynamic imports, default imports, js file import etc. You can find more information about dynamic imports here.

Solution 2 - Javascript

You can't resolve dynamically your dependencies, as imports are meant for static analysis. However, you can probably use some require here, something like:

for (let foo in bar) {
    if (bar.hasOwnProperty(foo)) {
        const Baz = require(foo).Baz;
    }
}

Solution 3 - Javascript

As this question is highly-ranked by Google, it is worth pointing out that things have changed since the older answers were posted.

MDN has this entry under Dynamic Imports:

> The import keyword may be called as a function to dynamically import a > module. When used this way, it returns a promise. > > import('/modules/my-module.js') > .then((module) => { > // Do something with the module. > }); > > // This form also supports the await keyword. > let module = await import('/modules/my-module.js');

A useful article on the subject can be found on Medium.

Solution 4 - Javascript

Since 2016 a lot has passed in JavaScript world, so I believe it's time to offer most updated info on this topic. Currently Dynamic imports are a reality both on Node and on browsers (natively if you don't care about IE, or with @babel/plugin-syntax-dynamic-import if you do care).

So, consider a sample module something.js with two named exports and one default export:

export const hi = (name) => console.log(`Hi, ${name}!`)
export const bye = (name) => console.log(`Bye, ${name}!`)
export default () => console.log('Hello World!')

We can use import() syntax to easily and cleanly load it conditionally:

if (somethingIsTrue) {
  import('./something.js').then((module) => {
    // Use the module the way you want, as:
    module.hi('Erick') // Named export
    module.bye('Erick') // Named export
    module.default() // Default export
  })
}

But since the return is a Promise, the async/await syntactic sugar is also possible:

async imAsyncFunction () {
  if (somethingIsTrue) {
    const module = await import('./something.js')
    module.hi('Erick')
  }
}

Now think about the possibilities along with Object Destructuring Assignment! For example, we are able to easily put only one of those named exports in memory for posterior use:

const { bye } = await import('./something.js')
bye('Erick')

Or maybe grab one of those named exports and rename it to anything else we want:

const { hi: hello } = await import('./something.js')
hello('Erick')

Or even rename the default exported function to something that makes more sense:

const { default: helloWorld } = await import('./something.js')
helloWorld()

Just a last (but no least) note: import() may looks like a function call, but it isn't a Function. It's a special syntax that just happens to use parentheses (similar to what happens with super()). So it's not possible to assign import to a variable or use things of the Function prototype, like call/apply.

Solution 5 - Javascript

Require will not solve your problem as it is a synchronous call. There are several options and they all involve

  1. Asking for the module you need
  2. Waiting for a promise to return the module

In ECMA Script there is support for lazy loading modules using SystemJS. This of course isn't supported in all browsers, so in the meantime you can use JSPM or a SystemJS shim.

https://github.com/ModuleLoader/es6-module-loader

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEnijarView Question on Stackoverflow
Solution 1 - JavascriptthecodejackView Answer on Stackoverflow
Solution 2 - JavascriptJonathan PetitcolasView Answer on Stackoverflow
Solution 3 - JavascriptLee GoddardView Answer on Stackoverflow
Solution 4 - JavascriptErick PetrucelliView Answer on Stackoverflow
Solution 5 - JavascriptHenrik VendelboView Answer on Stackoverflow