11

I'm new to ES6 and a bit confused with the way classes are exported and imported. It seems many different notations are valid but work differently.

I wrote a class like this in src/web-api.js:

class WebApi {
    // ...
}

export { WebApi };

Which I import with:

import { WebApi } from './src/web-api.js'

This works fine, but before I've tried the same thing without curly brackets and it didn't work:

export WebApi; // Tells me '{' expected

import WebApi from './src/web-api.js'; // No syntax error but WebApi is undefined

Even though on the MDN documentation for export, the notation export expression; appears to be valid.

Likewise, this is how React is imported in my application file:

import React, { Component } from 'react';

Why is one class with and another one without curly brackets? In general, how can I tell when to use and not to use curly brackets?

laurent
  • 88,262
  • 77
  • 290
  • 428
  • 2
    Possible duplicate of [When should I use curly braces for ES6 import?](http://stackoverflow.com/questions/36795819/when-should-i-use-curly-braces-for-es6-import) – Paolo May 06 '17 at 17:14

3 Answers3

16

ES6 offers many ways to manage modules through import/export. But there are basically two main strategies:

  1. Default export/import with export default and import module from './module'
  2. Multiple exports/imports with export and import {member} from './module' or import * as module from './module'

(Mixing both is possible but not recommended.)


Module to export/import

function foo() {
  console.log('Foo');
}

function bar() {
  console.log('Bar');
}

Strategy #1: Default export/import

Export (module.js)

function foo() {
  console.log('Foo');
}

function bar() {
  console.log('Bar');
}

export default {foo, bar};

/*
  {foo, bar} is just an ES6 object literal that could be written like so:

  export default {
    foo: foo,
    bar: bar
  };

  It is the legacy of the "Revealing Module pattern"...
*/

Import (main.js)

import module from './module';

module.foo(); // Foo
module.bar(); // Bar

Strategy #2: Multiple exports/imports

Export (module.js)

export function foo() {
  console.log('Foo');
}

export function bar() {
  console.log('Bar');
}

Import (main.js)

import {foo, bar} from './module';

foo(); // Foo
bar(); // Bar

/*
  This is valid too:

  import * as module from './module';

  module.foo(); // Foo
  module.bar(); // Bar
*/

As I said previously, ES6 modules are much more complex than that. For further information, I recommend you to read Exploring ES6 by Dr. Axel Rauschmayer, especially this chapter: http://exploringjs.com/es6/ch_modules.html.

Graham
  • 7,431
  • 18
  • 59
  • 84
Badacadabra
  • 8,043
  • 7
  • 28
  • 49
  • Thanks a lot for the detailed answer. Is there any consensus in the JS community on what's the preferred export strategy? – laurent May 08 '17 at 12:04
  • 1
    I don't think so. I would say that **multiple named exports** offer flexibility whereas a **single default export** offers stability. In Node.js apps (with CommonJS), a single exported object literal is quite popular (thanks to the Revealing Module pattern...). But honestly, this is mostly a matter of preference. Use one or another. Just avoid to use both in the same module, for the same reason you should avoid mixing snake case with camel case. It's possible but inconsistent. – Badacadabra May 08 '17 at 14:41
2

In your case, if you import from the src/web-api.js file without the curly braces, you should have anexport default something in the src/webfile-api.js

Without curly braces

class WebApi {...};
export default WebApi;

In your file

import WebApi from './src/web-api.js'

// Here, the element withexport default in the src/web-api.js file should be imported without the curly braces anywhere. PS: It must have only one export default for each file.

With curly braces

export { WebApi }

In your file import {WebApi} from './src/web-api.js'

Dan Abramov explains clearly the export/import methods in ES6 at this answer. When should I use curly braces for ES6 import?

Tolotra Raharison
  • 3,034
  • 1
  • 10
  • 15
1

The braces are just syntactic sugar. It will use the variable name as the object key, for example:

const a = 1;
const test = {a}; // same as {a: 1};

It can also be used to destructure the object by its variable name. It will check if the object has any properties with the same value as the variable name and then output a value if one is found:

const test = {a: 1};
const {a} = test; // a = 1

In modules, the general use case is that when you import there is usually braces since modules get imported as MODULE.function or MODULE.class. It'd be more intuitive to look at exports first:

For exporting, it's using the syntactic sugar I mentioned before - you're exporting it as an object. When you do export { WebApi }; what you're really doing is export {WebApi: WebApi}. This makes it easier to access things as you can just do 'MODULE.WebApi' now to access the class instead of having it pollute the namespace unnecessarily. This is also required for all exports!

Moving on to imports, what you're doing in the import statements is essentially destructuring the module object and picking a property to save into a variable of the same name. In your case, when you do import {WebApi} from './src/web-api.js' you'd be doing something like import WebApi = web-api.js['WebApi'] from './src/web-api.js' (this isn't valid syntax but just to show you what it's doing in the background). This is also required to properly import module functions/classes. There is also the option of importing the whole module, just as NodeJS does: import * as ModuleName from './src/module.js'. This will put all of exported functions/classes into the ModuleName object so that it can be treated as a normal module.

However, if a module has a default export, braces are not not needed for import and export. For example, react probably has export default React in its files - that's why there doesn't need to be braces around it when you do import React from 'react'

Hope I wasn't too confusing and let me know if I can clarify anything!

jwoos
  • 149
  • 1
  • 4
  • 2
    The reference to object destructuring is inappropriate. `import` syntax is self-contained and *tries to mimic* destructuring syntax (to the point where it's applicable). It's not destructuring and it's syntactic sugar for nothing. – Estus Flask May 06 '17 at 19:22
  • So sum’up your answer ….However, if a module has a default export, braces are not not needed for import and export. – Ray Carnegie Aug 27 '23 at 10:14