コンポーネントのインポートとエクスポート

コンポーネントの魅力は再利用のしやすさにあります。他のコンポーネントを組み合わせて新しいコンポーネントを作ることができるのです。しかし、コンポーネントのネストが増えてくると、それらを別のファイルに分割したくなってきます。これにより、欲しいファイルを簡単に見つけ出し、より多くの場所でコンポーネントを再利用できるようになります。

このページで学ぶこと

  • ルートコンポーネントファイルとは何か
  • コンポーネントのインポート・エクスポートの方法
  • デフォルトインポート/エクスポートと名前付きインポート/エクスポートの使い分け
  • 1 つのファイルから複数のコンポーネントをインポート・エクスポートする方法
  • コンポーネントを複数のファイルに分割する方法

ルートコンポーネントファイル

初めてのコンポーネントでは、Profile コンポーネントと、それをレンダーする Gallery コンポーネントを作成しました。

function Profile() {
return (
<img
src="https://i.imgur.com/MK3eW3As.jpg"
alt="Katherine Johnson"
/>
);
}

export default function Gallery() {
return (
<section>
<h1>Amazing scientists</h1>
<Profile />
<Profile />
<Profile />
</section>
);
}

Error

Extra 185 of 186 byte(s) found at buffer[1]

これらのコンポーネントは今のところ、ルート (root) コンポーネントファイル(この例では App.js という名前)に置かれています。セットアップによっては、ルートコンポーネントは別のファイルに存在するかもしれません。Next.js のようなファイルベースのルーティングがあるフレームワークを使っている場合、ルートコンポーネントはページごとに異なるものになります。

コンポーネントのエクスポートとインポート

もし将来ランディングページを変更して科学書のリストを表示したくなった場合はどうでしょうか。あるいはプロフィールを別の場所に表示したくなった場合は? GalleryProfile はルートコンポーネントファイル以外の場所に置きたくなるでしょう。これによりコンポーネントはよりモジュール化され、他のファイルから再利用可能になります。コンポーネントの移動は以下の 3 ステップで行えます。

  1. コンポーネントを置くための新しい JS ファイルを作成する
  2. デフォルトエクスポートあるいは名前付きエクスポートのいずれかを使い、関数コンポーネントをそのファイルからエクスポートする
  3. そのコンポーネントを、利用する側のファイルでインポートする。デフォルトエクスポートされたのか名前付きエクスポートされたのかに応じて、対応するインポート手法を使う。

以下の例では、ProfileGallery の両方が、App.js から Gallery.js という新しいファイルに移動しています。App.js を書きかえて、Gallery.js から Gallery をインポートするようにできます。

import Gallery from './Gallery.js';

export default function App() {
  return (
    <Gallery />
  );
}

Error

Extra 185 of 186 byte(s) found at buffer[1]

元の例がどのようにして 2 つのコンポーネントファイルに分割されたか確認しましょう。

  1. Gallery.js は:
    • Profile を定義しているが、これは同一ファイル内でしか使われてないのでエクスポートしていない。
    • デフォルトエクスポートとして Gallery コンポーネントをエクスポートしている。
  2. App.js は:
    • Gallery.js から Galleryデフォルトインポートしている。
    • ルートの App コンポーネントをデフォルトエクスポートしている。

補足

.js というファイル拡張子が省略された、以下のようなファイルを見ることがあるかもしれません。

import Gallery from './Gallery';

React では './Gallery.js' でも './Gallery' でも動作しますが、前者の方がネイティブ ES モジュールの動作により近い方法です。

さらに深く知る

デフォルトエクスポート vs 名前付きエクスポート

JavaScript には値をエクスポートする主な方法が 2 つあります。デフォルトエクスポートと名前付きエクスポートです。これまで、我々の例ではデフォルトエクスポートのみを使ってきました。しかし同じファイルで両方を使うことも、あるいはどちらか片方だけを使うことも可能です。ファイルにはデフォルトエクスポートは 1 つまでしか置けませんが、名前付きエクスポートは好きなだけ置くことができます

デフォルトエクスポートと名前付きエクスポート

どのようにコンポーネントをエクスポートするかによって、それをどのようにインポートするのかが決まります。デフォルトエクスポートされたものを名前付きエクスポートのようにインポートしようとするとエラーになります! 以下の表が参考になるでしょう。

構文Export 文Import 文
Defaultexport default function Button() {}import Button from './Button.js';
Namedexport function Button() {}import { Button } from './Button.js';

デフォルトインポートを書く場合、import の後には好きな名前を書くことができます。例えば import Banana from './Button.js' と書いたとしても、デフォルトエクスポートされている同じ値を得ることができます。一方で、名前付きエクスポートでは、エクスポート側とインポート側で名前が合致していなければなりません。だからこそ名前付きエクスポートと呼ばれるわけですね。

ファイルがコンポーネントを 1 つだけエクスポートする場合はデフォルトエクスポートが、複数のコンポーネントや値をエクスポートする場合は名前付きエクスポートがよく使われます。どちらのコーディングスタイルが好みの場合でも、コンポーネントやそれが入るファイルには、常に意味の通った名前を付けるようにしてください。export default () => {} のような名前のないコンポーネントは、デバッグが困難になるため推奨されません。

同じファイルから複数のコンポーネントをエクスポート・インポートする

ギャラリではなく、Profile を 1 つだけを表示したい場合はどうでしょう。Profile コンポーネントもエクスポートすればいいのです。しかし、Gallery.js にはすでにデフォルトエクスポートがあり、デフォルトエクスポートは 2 つ以上存在できません。デフォルトエクスポートを持つ新しいファイルを作成するか、または Profile 用の名前付きエクスポートを追加することができます。1 つのファイルはデフォルトエクスポートを 1 つしか持つことができませんが、名前付きエクスポートはたくさんあっても構いません!

補足

デフォルトエクスポートと名前付きエクスポートとで混乱する可能性を減らすために、チームによっては 1 つのスタイル(デフォルトまたは名前付き)に統一したり、1 つのファイルでこれらを混在させないようにしています。これは好みの問題です。自分たちに合った方法を選んでください!

まずは Gallery.jsProfile を名前付きでエクスポートします(default キーワードは付けない)。

export function Profile() {
// ...
}

そして、Gallery.jsProfileApp.js に名前付きでインポートします(中括弧を使う)。

import { Profile } from './Gallery.js';

最後に App コンポーネントで <Profile />レンダーします。

export default function App() {
return <Profile />;
}

これで Gallery.js には、デフォルトの Gallery というエクスポートと、名前付きの Profile というエクスポートの 2 つが含まれるようになりました。App.js はその両方をインポートしています。この例の <Profile /><Gallery /> と書きかえたり、元に戻したりしてみてください。

import Gallery from './Gallery.js';
import { Profile } from './Gallery.js';

export default function App() {
  return (
    <Profile />
  );
}

これで、デフォルトと名前付きのエクスポートが混在するようになりました。

  • Gallery.js は:
    • Profile コンポーネントを Profile という名前付きでエクスポートしている
    • Gallery コンポーネントをデフォルトエクスポートしている。
  • App.js は:
    • Profile コンポーネントを Gallery.js から Profile という名前付きでインポートしている
    • Gallery コンポーネントを Gallery.js からデフォルトインポートしている。
    • ルートの App コンポーネントをデフォルトエクスポートしている。

まとめ

このページでは以下のことを学びました。

  • ルートコンポーネントファイルとは何か
  • コンポーネントのインポート・エクスポートの方法
  • デフォルトインポート/エクスポートと名前付きインポート/エクスポートの使い分け
  • 1 つのファイルから複数のコンポーネントをインポート・エクスポートする方法

チャレンジ 1/1:
コンポーネントファイルをさらに分割する

現在のところ Gallery.jsProfileGallery の両方をエクスポートしていますが、これはちょっと混乱の原因になりそうです。

Profile コンポーネントを Profile.js という別ファイルに移動し、その後で App コンポーネントも変更して <Profile /><Gallery /> を並べてレンダーするようにしてください。

Profile をエクスポートするのにデフォルトと名前付きのどちらの手法を使っても構いませんが、App.jsGallery.js の両方で対応するインポート構文を使うようにしましょう! 上記の詳細セクションで挙げたこちらの表を参照しても構いません:

構文Export 文Import 文
Defaultexport default function Button() {}import Button from './Button.js';
Namedexport function Button() {}import { Button } from './Button.js';
// Move me to Profile.js!
export function Profile() {
  return (
    <img
      src="https://i.imgur.com/QIrZWGIs.jpg"
      alt="Alan L. Hart"
    />
  );
}

export default function Gallery() {
  return (
    <section>
      <h1>Amazing scientists</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  );
}

片方のエクスポート構文でうまく動かせたら、もう片方の構文でも動くようにしてみましょう。