When should I use curly braces for ES6 import?

When should I use curly braces for ES6 import?

It seems to be obvious, but I found myself a bit confused about when to use curly braces for importing a single module in ES6. For example, in the React-Native project I am working on, I have the following file and its content:

initialState.js

var initialState = {
todo: {
todos: [
{id: 1, task: ‘Finish Coding’, completed: false},
{id: 2, task: ‘Do Laundry’, completed: false},
{id: 2, task: ‘Shopping Groceries’, completed: false},
]
}
};

export default initialState;

In the TodoReducer.js, I have to import it without curly braces:
import initialState from ‘./todoInitialState’;

If I enclose the initialState in curly braces, I get the following error for the following line of code:

Cannot read property todo of undefined

TodoReducer.js:

export default function todos(state = initialState.todo, action) {
// …
}

Similar errors also happen to my components with the curly braces. I was wondering when I should use curly braces for a single import, because obviously, when importing multiple component/modules, you have to enclose them in curly braces, which I know.
Edit:
The SO post at here does not answer my question, instead I am asking when I should or should not use curly braces for importing a single module, or I should never use curly braces for importing a single module in ES6 (this is apparently not the case, as I have seen single import with curly braces required)

Solutions/Answers:

Solution 1:

This is a default import:

// B.js
import A from './A'

It only works if A has the default export:

// A.js
export default 42

In this case it doesn’t matter what name you assign to it when importing:

// B.js
import A from './A'
import MyA from './A'
import Something from './A'

Because it will always resolve to whatever is the default export of A.


This is a named import called A:

import { A } from './A'

It only works if A contains a named export called A:

export const A = 42

In this case the name matters because you’re importing a specific thing by its export name:

// B.js
import { A } from './A'
import { myA } from './A' // Doesn't work!
import { Something } from './A' // Doesn't work!

To make these work, you would add a corresponding named export to A:

// A.js
export const A = 42
export const myA = 43
export const Something = 44

A module can only have one default export, but as many named exports as you’d like (zero, one, two, or many). You can import them all together:

// B.js
import A, { myA, Something } from './A'

Here, we import the default export as A, and named exports called myA and Something, respectively.

// A.js
export default 42
export const myA = 43
export const Something = 44

We can also assign them all different names when importing:

// B.js
import X, { myA as myX, Something as XSomething } from './A'

The default exports tend to be used for whatever you normally expect to get from the module. The named exports tend to be used for utilities that might be handy, but aren’t always necessary. However it is up to you to choose how to export things: for example, a module might have no default export at all.

This is a great guide to ES modules, explaining the difference between default and named exports.

Solution 2:

TL;DR: Curly braces are used if you would like to import a non-default export.

See Dan Abramovs answer above for more details.

Solution 3:

I would say there is also a starred notation for the import ES6 keyword worth to mention.

enter image description here

If you try to console log Mix:

import * as Mix from "./A";
console.log(Mix);

You will get:

enter image description here

When should I use curly braces for ES6 import?

The brackets are golden when you need only specific components from the module, which makes smaller footprints for bundlers like webpack.

Solution 4:

Dan Abramov answer above explains about the default exports and named exports.

Which to use?

Quoting David Herman: ECMAScript 6 favors the single/default export style, and gives the sweetest syntax to importing the default. Importing named exports can and even should be slightly less concise.

However in TypeScript named export is favored because of refactoring. Example, if you default export a class and rename it, the class name will change only in that file and not in the other references, with named exports class name will be renamed in all the references.
Named exports is also preferred for utilities.

Overall use whatever you prefer.

Additional

Default export is actually a named export with name default, so default export can be imported as:

import {default as Sample} from '../Sample.js';

Solution 5:

If you think of import as just syntax sugar for node modules, objects, and destructuring, I find it’s pretty intuitive.

// bar.js
module = {};

module.exports = { 
  functionA: () => {},
  functionB: ()=> {}
};

 // really all that is is this:
 var module = { 
   exports: {
      functionA, functionB
   }
  };

// then, over in foo.js

// the whole exported object: 
var fump = require('./bar.js'); //= { functionA, functionB }
// or
import fump from './bar' // same thing, object functionA and functionB props


// just one prop of the object
var fump = require('./bar.js').functionA;

// same as this, right?
var fump = { functionA, functionB }.functionA;

// and if we use es6 destructuring: 
var { functionA } =  { functionA, functionB };
// we get same result

// so, in import syntax:
import { functionA } from './bar';

Solution 6:

In order to understand the use of curly braces in import statements, first, you have to understand the concept of destructing introduced in ES6

  1. Object destructuring

    var bodyBuilder = {
      firstname: 'Kai',
      lastname: 'Greene',
      nickname: 'The Predator'
    };
    
    var {firstname, lastname} = bodyBuilder;
    console.log(firstname, lastname); //Kai Greene
    
    firstname = 'Morgan';
    lastname = 'Aste';
    
    console.log(firstname, lastname); // Morgan Aste
    
  2. Array destructuring

    var [firstGame] = ['Gran Turismo', 'Burnout', 'GTA'];
    
    console.log(firstGame); // Gran Turismo
    

    Using list matching

      var [,secondGame] = ['Gran Turismo', 'Burnout', 'GTA'];
      console.log(secondGame); // Burnout
    

    Using the spread operator

    var [firstGame, ...rest] = ['Gran Turismo', 'Burnout', 'GTA'];
    console.log(firstGame);// Gran Turismo
    console.log(rest);// ['Burnout', 'GTA'];
    

Now that we’ve got that out of our way, in ES6 you can export multiple modules. You can then make use of object destructuring like below

Let’s assume you have a module called module.js

    export const printFirstname(firstname) => console.log(firstname);
    export const printLastname(lastname) => console.log(lastname);

You would like to import the exported functions into index.js;

    import {printFirstname, printLastname} from './module.js'

    printFirstname('Taylor');
    printLastname('Swift');

You can also use different variable names like so

    import {printFirstname as pFname, printLastname as pLname} from './module.js'

    pFname('Taylor');
    pLanme('Swift');