Reactのexport defaultとexportって何がどう違うんや
発端の話
業務でReactを触って開発を進めていく中で、以下のようなコードをよく見かけました。
export default function Button() {
return <button>押す</button>;
}
一方でこんなコードも見かけました。
export function Button() {
return <button>押す</button>;
}
この例は、どちらも「外部からButtonを使えるようにする」ための書き方です。
Reactド初心者の私は、違いはよく知らんけどテストしてみて動いたから一旦ヨシ!
…と実装を進めたら、案の定エラーが出てきて詰まるという状況に陥りました。
(我ながら本当に良くない、、)
- export defaultはただのexportと何がどう違うんや??
- 結局どっち使えばええんや??
私と同じように、このあたりがフワッとしてる人がいるだろうな~と思ったので、
忘備録&まとめとして記事に残してみようと思いました。
本題に入る前に
- ファイル1つ1つが
モジュール - その
モジュールから外に出すものがexport - 逆に他の
モジュールが出してくれたものがimport
上記のような仕組み・関係図となっており、
このうちのexportが下記のようにザックリ2種類あって、
そいつらの性格はこう違うよ~というのが今回のテーマです。
- export default(デフォルトexportとも呼ばれているやつ)
- export(名前付きexportとかnamed exportとも呼ばれているやつ)
export defaultってなんぞや
このファイルの中で扱ってる商品はコレだけや!
というのがexport defaultのイメージでOKだと思います。
最初の例と同じですが書き方はこちら。
export default function Button() {
return <button>押す</button>;
}
これを使う側はこのように書きます。
// Buttonをimportする
import Button from "./components/Button";
// importしたButtonを使う
export default function Page() {
return (
<div>
<Button />
</div>
);
}
ポイントは3つ。
- 1ファイルに複数の
export defaultは書けない -
importする際は{}なし(後述のexportでは必要です) -
importする側で 好きな名前をつけられる
例えばこんなこともできます。(ややこしくなるからやらないほうが良さそうだけど)
// 中身はButtonだけど名前はHogeに変更
import Hoge from "./Button";
以下のようにexport defaultを複数書くことはできません。
export default function Button1() {
return <button>押す</button>;
}
// export defaultは1ファイル1つのみなのでNG
export default function Button2() {
return <button>押す</button>;
}
export(名前付きexport)ってなんぞや
このファイルの中にこんな商品とあんな商品があるで!
という商品カタログ的なものが名前付きexportのイメージです。
例えば2つのボタンをexportで用意したら
export function ClickButton() {
return <button>押す</button>;
}
export function DangerButton() {
return <button>押さないで</button>;
}
使う側はこうなる
// importは{}が必要
import { ClickButton, DangerButton } from "./components/Button";
// importしたClickButtonとDangerButtonを使う
export default function Page() {
return (
<>
<ClickButton />
<DangerButton />
</>
);
}
ポイントは3つ。
- 1ファイルに複数
exportを書ける(単独exportでもOK) - import { ... } from で読み込む
- 名前が完全一致してないとダメ(大文字小文字も識別されます)
というところです。
※ 名前の完全一致関連については、以下のようにasを使えば別名を付けることもできます。
// 別名でimportできる
import { ClickButton as Button } from "./components/Button";
export default function Page() {
return (
<div>
<Button />
</div>
);
}
結局どういう時にどっちを使えばええんや??
じゃあ結局この2つのexportはどう使い分ければええんや??という話ですが、
- 1ファイル1コンポーネント派 →
export default - 複数コンポーネントを1ファイルにまとめたい派 → 名前付き
export
でいいんじゃないかと思いました。
もしくは1プロジェクト内で2つの書き方が混在するとややこしいので、exportのみを使うとか。
ここを上手い具合に運用している方がいらっしゃいましたら、ぜひ教えていただきたいです。
まとめ
export default
- 1ファイル1つまで
- import Hoge from "./hoge";
- 使う側で好きな名前をつけられる
export(名前付きexport)
- 複数OK
- import { Hoge, HogeHoge } from "./hoge";
- 名前はファイル側・使う側で一致させる必要あり(asで別名OK)
Reactでの使い分け
1ファイル1コンポーネント → export default
複数コンポーネント → 名前付きexport
上記のような感覚を持っておけば、
export defaultとexportのモヤモヤはだいぶ減るかと思います。
余談(NGケース)
私が今回書き方を間違えて出したエラーメッセージがこちらです。
⨯ Internal error: Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
これはexport defaultとexportの使い方を間違えた時に
よく出るエラーですがそれだけではなく、
コンポーネントに不正な値を渡したとき全般で出るエラーとなります。
私が実際にやらかしたのが以下のようなケースでした。
- importパスを間違えていて、undefinedが来ている
import Button from "./Buton"; // Buttonのスペルミス
- コンポーネントじゃないオブジェクトをJSXに渡している
const components = { Button };
export function Page() {
return <components />; // componentsはオブジェクトなのでNG
}
Discussion
(書き方はいろいろとありますが、)複数の同名の名前つき export が書けないので
単に 変数にできない default と言う予約語の名前の 名前つき export みたいなニュアンスかと
( import() メソッドを使うとよくわかります。
同名の名前付き
exportは複数書けない →defaultという名前のexportもそのルールに乗っているだけ、確かになるほど…!と納得できました。自分の中での
export defaultは「特別なもの」程度のふわっとした理解だったので、defaultという名前のexportが1個だけ許されている、という見方をしていました、、コメントいただきありがとうございます!