ぬにょす(挨拶)。
今回紹介するのは、Next.js が提供する dynamic 関数を使って変数で指定したコンポーネントを読み込む方法です。
結論のコード
// 変数 component には、事前にコンポーネント名が格納されている
const DynamicComponent = dynamic(() =>
  import("./module").then((mod: any) => mod[component])
);Code language: TypeScript (typescript)はじめに
Next.js には dynamic import という機能があります。
当該コンポーネントの表示が必要になったタイミングで import することで、最初のページ読み込みにおける転送量を減らすことができます。
この dynamic import で読み込むコンポーネントを変数で指定できないものかと思った次第です。
要するに
switch (arg) {
  case "A":
    DynamicComponent = dynamic(() => import("./A"));
    break;
  case "B":
    DynamicComponent = dynamic(() => import("./B"));
    break;
  case "C":
    DynamicComponent = dynamic(() => import("./C"));
    break;
}Code language: TypeScript (typescript)みたいなことを
DynamicComponent = dynamic(() => import(`./${arg}`))Code language: TypeScript (typescript)のように書けないもんかな、と。
import()に変数を渡してもダメっぽい
最初はこれでイケるんじゃないかと思ったのですが。
// 何らかの事前処理で modulePath に "../components/hello" をセットした上で
const DynamicComponent = dynamic(() => import(modulePath))Code language: JavaScript (javascript)期待通りの動きにはなりませんでした。
変数経由だと、コンパイルなりビルドなりの時点では読み込むモジュール名が不明ですからね。
生成された js ファイルに、当該モジュールのコードが入らないんだと推測してます。
名前つきexportを応用してみた
Next.js 公式サイトの dynamic import のページには、名前つきで export されたコンポーネントを dynamic import する例が載っています。
以下、一部引用。
const DynamicComponent = dynamic(() =>
  import('../components/hello').then((mod) => mod.Hello)
)Code language: TypeScript (typescript)“../components/hello” を読み込んだ後、モジュールに含まれる “Hello” というコンポネントを返却しているように解釈できます。
mod.Hello は mod オブジェクトの Hello メンバにアクセスしているわけだから
// 何らかの事前処理で moduleName に "Hello" をセットした上で
const DynamicComponent = dynamic(() =>
  import('../components/hello').then((mod) => mod[moduleName])
)Code language: JavaScript (javascript)という書き方と等価なんじゃないか?と思って試してみたらビンゴでした。
まとめ
- import() の引数に変数を渡してもダメ。
- import() の引数には文字列リテラルを指定し、使用するファイル(モジュール)を明確にした上で読み込むコンポーネントを変数で指定することは可能。
後書き
何故このようなコードが必要になったかというと、react-icons パッケージのアイコン一覧を表示する必要があったからです。
100を超える import を書く気はしなかったので、外部jsonファイルから読み込んだコンポーネント名を使って import できないかと試行錯誤した結果、今回紹介した方法を見つけた次第です。



コメント