エラーメッセージ
真っ白な画面を前に、コンソールには赤文字のエラーが並んでいる。これは、すべてのReact開発者が通る道です。通常、エラーは次のように表示されます。
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.
ブラウザが実際に伝えていること
Reactは本質的に、「何かをレンダリングするように指示されましたが、それが何なのか全くわかりません」と言っています。<MyComponent />と記述すると、Reactはその変数を評価します。もしそれがundefinedであれば、レンダリングエンジンはクラッシュします。Reactが理解できるのは、HTMLタグ('div'や'span'など)、または実際のJavaScriptの関数やクラスのみです。
この問題のほとんどは、importとexportの記述が一致していないことが原因で発生します。一方のファイルがパッケージを送信していても、もう一方がそれを正しく受信できていない状態です。
1. 原因の90%:Default Import vs. Named Imports
素早いリファクタリングの最中に、このミスが起きるのをよく目にします。コンポーネントがdefaultとしてエクスポートされている場合、インポート時に波括弧({})を使用することはできません。名前付きエクスポート(named export)の場合は、波括弧を使用する必要があります。
シナリオA:Default Exportの罠
export defaultを使用すると、ファイル全体を一つの単位として送信することになります。
誤り:
// MyComponent.js
export default function MyComponent() { return <div>Hello</div>; }
// App.js
import { MyComponent } from './MyComponent'; // エラー!ここでMyComponentはundefinedになります。
正解:
import MyComponent from './MyComponent';
シナリオB:Named Exportの不一致
名前付きエクスポートでは、一つのファイルから複数のものをエクスポートできますが、正確さが求められます。
誤り:
// MyComponent.js
export const MyComponent = () => <div>Hello</div>;
// App.js
import MyComponent from './MyComponent'; // エラー!存在しないデフォルトエクスポートを探しています。
正解:
import { MyComponent } from './MyComponent';
2. 「バレルファイル」の悩み (index.js)
フォルダのインポートを整理するためにindex.jsファイルを使用するのは優れたパターンですが、壊れると厄介です。./components/Buttonではなく./componentsからインポートする場合、index.jsが完璧にマッピングされている必要があります。
次のような構造を考えてみましょう:
components/
├── Button.js (using export default)
└── index.js
index.jsは次のようになっている必要があります:
export { default as Button } from './Button';
このファイルの記述が1行足りないだけで、アプリケーションはundefinedをレンダリングしようとしてしまいます。新しいUIコンポーネントを追加したときは、他の場所で使用する前に、バレルファイルに実際に追加したかどうかを再確認してください。
3. 循環参照:無限ループ
これはこのエラーの中で最も厄介なパターンです。コンポーネントAがコンポーネントBをインポートし、かつコンポーネントBもコンポーネントAをインポートしている場合、モジュールシステムがループの解析を完了できないため、どちらかがundefinedとしてロードされます。
ループが疑われる場合は、共有ロジックや小さなサブコンポーネントをSharedUtils.jsのような3番目のファイルに移動してください。これにより循環が断ち切られ、両方のコンポーネントが依存関係を安全にインポートできるようになります。
4. 本番環境における大文字・小文字の区別
macOSでのローカル開発は、多くの場合、大文字と小文字を区別しません。実際のファイル名がMyButton.jsであるのに./Mybuttonとしてインポートしても、自分のPCでは正常に動作するかもしれません。しかし、VercelやAWSのようなLinux環境にデプロイすると、ビルドが失敗するか、参照がundefinedになります。
常にファイル名の大文字・小文字を正確に一致させてください。これにより、「自分のマシンでは動くのに」という問題のデバッグに費やす時間を節約できます。
30秒でできる確認方法
推測に頼ってはいけません。コンポーネントが壊れる直前でconsole.logを使用してください。これが疑念を確認する最短の方法です。
import { MyComponent } from './MyComponent';
// ブラウザのコンソールでこれを確認してください
console.log("Is MyComponent defined?", MyComponent);
const App = () => {
return <MyComponent />;
};
ログにundefinedと表示されれば、問題はJSXの構文ではなく、import/exportの橋渡しにあることが確認できます。
予防のためのプロのヒント
- 名前付きエクスポートを徹底する: 私は
export const MyComponent...を好んで使います。なぜなら、IDEがインポートを100%正しく自動補完してくれるからです。 - TypeScriptを採用する: TypeScriptは「保存」を押す前にこのエラーをキャッチします。ソースファイルに存在しないインポートは許可されません。
- サードパーティのアップデートに注意する: ライブラリのメジャーバージョンアップでは、エクスポートの方法が変更されることがよくあります。最近
framer-motionやlucide-reactをアップデートした場合は、名前付きエクスポートがデフォルトエクスポートに変更されていないか(あるいはその逆か)、ドキュメントを確認してください。

