chevron-left chevron-right

Piotr Nalepa Frontend, architektura i AI

Is using default exports considered a bad practice? Here’s why!

Ta wersja
blog.piotrnalepa.pl/en/2020/06/26/default-exports-vs-named-exports
Opublikowano
Autor
, Staff Software Engineer
Kategoria
JavaScript
Uwagi
Napisz do mnie · Napisz na X

Ten wpis ma 6 lat. Od tego czasu przeglądarki i narzędzia mogły się zmienić, więc zanim użyjesz kodu, sprawdź go z aktualną dokumentacją.

When writing React apps or modern JavaScript in general, we often create files with a set of reusable functions or classes. People tend to use default exports in such cases and I'm going to explain, why it's not the best idea to use them.

Sekcja 1What is a default export?

The origin of default exports comes from a willingness to replicate the functionality of exporting modules from CommonJS spec to ES6. So what is a default export basically? Let's have a look at the following piece of code:

Przykład 1JavaScript
// src/components/SomeComponent.js
export default class SomeComponent {
    ...
}

So you can import it into another file like in the following example:

Przykład 2JavaScript
// src/App.js
import SomeComponent from './components/SomeComponent';

After importing it you can use it however you like in the code of App.js file. The important thing to note is that you don't have to use SomeComponent as a name of imported code. It can be literally anything, because there's no strict convention here.

Sekcja 2What is a named export?

Comparing to the default exports example it looks differently. First of all there's no default keyword.

Przykład 3JavaScript
// src/components/SomeComponent.js
class SomeComponent {
    ...
}

export const OtherComponent = () => {
    ...
}

export { SomeComponent };

So you can import it into another file like in the following example:

Przykład 4JavaScript
// src/App.js
import { SomeComponent, OtherComponent } from './components/SomeComponent';

So what are the main differences between both approaches and why you might consider using default exports as a bad practice? The explanation is in the next section.

Sekcja 3The fight

Let's compare both approaches. I'll try to explain you which of these approaches is better and why.

Sekcja 3.1Naming convention strictness

Like I mentioned earlier, in the default export section, using default exports and importing into another file, you are not restricted to use a component name (or class name, or whatever) defined in the definition file. While importing you can change its name into anything that you find reasonable.

Przykład 5JavaScript
// src/components/SomeComponent.js
export default class SomeComponent { ... }

// src/App.js
import AnyComponent from './components/SomeComponent'

In case of named exports it's not so easy to use any name for imported piece of code. You are restricted to use names defined in the definition file of a piece code.

Przykład 6JavaScript
// src/components/SomeComponent.js
class SomeComponent { ... }

export { SomeComponent };

// src/App.js
import { SomeComponent } from './components/SomeComponent';

But what if you want to change the name of imported component? It's not complicated:

Przykład 7JavaScript
// src/components/SomeComponent.js
class SomeComponent { ... }

export SomeComponent;

// src/App.js
import { SomeComponent as AnyComponent } from './components/SomeComponent';

It might be useful when importing components with similar names from different sources. In the case when you're exporting many components with similar names from many different files, it might be useful to set some sort of namespace for them. Let's take a look:

Przykład 8JavaScript
// src/components/SomeComponent.js
class Button { ... }

export const Panel = () => {};
export const config = {};

export Button;

// src/App.js
import * as UI from './components/SomeComponent';

function App () {
    doSomething(UI.config);

    return (
        <UI.Panel>
            <h1>Title</h1>
            <UI.Button />
        </UI.Panel>
    )
}

The last way of importing components is commonly used in React environment for importing React library in TypeScript projects:

Przykład 9JavaScript
import * as React from 'react'

In my opinion having multiple ways of importing components versus one unreliable way of importing them is much better in terms of bulding quality apps. You can accomodate imports to your needs.

Sekcja 3.2Error reporting

Spotting errors early is extremely important in software development. From my experience using default exports is error-prone solution because you don't know whether a specific piece of code exists in a file. When I'm using named exports my code editor can early spot errors by checking whether an imported component exists in a source file. I don't have to worry about mistyping component names. And there we are, typos, these are the most problematic errors, because sometimes they tend to be different to spot a first glance, especially when doing code reviews in apps like Github or Gitlab.

Sekcja 3.3Long term code mainteinance

This one is pretty important when building professional apps, not just pet projects that will never get into the public. Choosing the right exporting convention, I mean using named exports over default exports increases code maintainability a lot. Let's imagine a situation when you're doing a code refactor. You're looking for occurences of a specific component in a codebase. With named exports it's pretty simple, because the imported component name usually is the same as exported one. With default exports it's not that easy, the component names might differ, sometimes a lot. It can have a different name, the name can have a typo. It's all confusing and makes code mainteinance harder.

Sekcja 4Default exports as necessary evil sometimes

Sometimes you might be forced to use default exports anyway. Because of other tools that are used when developing React apps. Such tool's developers didn't accomodate it for using code exported using named exports convention. For instance when lazy loading React components. It's a code design decision made by React team. At the end you have to write something like the following, in order to lazy load your React component:

Przykład 10JavaScript
// e.g. App.js
const LazyComponent = React.lazy(() => import('./LazyComponent'));

function App {
    return (
        <div>
            <Suspense fallback={<div>Loading ...</div>}>
                <LazyComponent />
            </Suspense>
        </div>
    )
}

Sekcja 5Summary

In projects I'm working on I'm using named exports because it makes a codebase more predictable and less error-prone. I prefer getting errors as soon as possible when exporting non-existing components and I love having classes names and component names the same as in the their source files. It helps me a lot in reading the code.

Udostępnij
Uwagi

O autorze

Staff Software Engineer

Piszę na tym blogu od 2009 roku, a strony i aplikacje webowe tworzę zawodowo od 14 lat. Obecnie w Bishop Fox prowadzę frontend platformy Cosmos, od samego początku projektu. Wcześniej pracowałem m.in. w OLX, KUDO, Auto1 i eZ Systems. Piszę tu o tym, czego nauczyłem się w praktyce: o JavaScripcie, TypeScripcie, Reakcie, CSS-ie i architekturze frontendu. Występowałem też jako prelegent na konferencjach w Polsce i Europie. Masz pytanie albo pomysł na wpis? Daj znać!