Reactでエラーが出た時の対処法|初心者向けデバッグ術
React開発でよくあるエラーの原因と解決方法を初心者向けに解説。エラーメッセージの読み方からデバッグテクニックまで、実践的な対処法を紹介
みなさん、Reactでコードを書いていて赤いエラーメッセージが出て困ったことはありませんか?
「何が原因なのか全然分からない」「エラーメッセージが英語で理解できない」「どこを直せばいいのか見当もつかない」といった悩みを抱えている方も多いでしょう。
この記事では、React初心者の方でも安心してエラーに対処できるよう、よくあるエラーの解決方法と効果的なデバッグのコツをご紹介します。 エラーが出ても慌てる必要はありません。一緒に解決していきましょう!
エラーメッセージの読み方をマスターしよう
まずは、エラーメッセージを正しく読み解く方法を覚えましょう。
エラーメッセージの構造を理解する
典型的なエラーメッセージはこんな感じです:
Error: Cannot read property 'name' of undefined
at UserProfile (UserProfile.js:5:20)
at App (App.js:12:8)
at ReactDOM.render (index.js:8:3)
このエラーメッセージから分かることは以下の通りです:
- エラーの内容:
Cannot read property 'name' of undefined
- エラーが起きた場所:UserProfile.jsの5行目20列目
- 呼び出し元:App.jsの12行目8列目
簡単に言うと、「undefined
のname
プロパティを読もうとしてエラーになった」ということです。
実際のコードで確認してみよう
エラーメッセージと実際のコードを見比べてみましょう:
// UserProfile.js
function UserProfile({ user }) {
return (
<div>
<h2>{user.name}</h2> {/* 5行目: userがundefinedだとエラー */}
<p>{user.email}</p>
</div>
);
}
// App.js
function App() {
const user = undefined; // 12行目: userが定義されていない
return <UserProfile user={user} />; // エラーの原因
}
エラーメッセージを見れば、どこで何が起きているのかが分かりますね。 最初は慣れないかもしれませんが、少しずつ読めるようになってきます。
よくあるエラーと解決方法
Reactでよく遭遇するエラーパターンを、解決方法と一緒に見ていきましょう。
1. Cannot read property of undefined
これは一番よく見るエラーです。
オブジェクトがundefined
やnull
なのに、そのプロパティにアクセスしようとすると発生します。
// ❌ エラーが発生するコード
function UserProfile({ user }) {
return (
<div>
<h2>{user.name}</h2> {/* userがundefinedの場合エラー */}
<p>{user.email}</p>
</div>
);
}
解決方法その1:条件付きレンダリング
// ✅ 解決方法1: データがない時は別の表示をする
function UserProfile({ user }) {
if (!user) {
return <div>ユーザー情報を読み込み中...</div>;
}
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
解決方法その2:デフォルト値を設定
// ✅ 解決方法2: 初期値を設定する
function UserProfile({ user = {} }) {
return (
<div>
<h2>{user.name || '名前未設定'}</h2>
<p>{user.email || 'メール未設定'}</p>
</div>
);
}
解決方法その3:オプショナルチェイニング
// ✅ 解決方法3: ?. を使って安全にアクセス
function UserProfile({ user }) {
return (
<div>
<h2>{user?.name || '名前未設定'}</h2>
<p>{user?.email || 'メール未設定'}</p>
</div>
);
}
2. Objects are not valid as a React child
オブジェクトをそのまま表示しようとした時のエラーです。
// ❌ エラーが発生するコード
function UserProfile({ user }) {
return (
<div>
<h2>ユーザー情報</h2>
<p>{user}</p> {/* オブジェクトをそのまま表示しようとしてエラー */}
</div>
);
}
解決方法その1:特定のプロパティを表示
// ✅ 解決方法1: オブジェクトの中身を個別に表示
function UserProfile({ user }) {
return (
<div>
<h2>ユーザー情報</h2>
<p>名前: {user.name}</p>
<p>メール: {user.email}</p>
</div>
);
}
解決方法その2:JSON.stringifyで文字列化
// ✅ 解決方法2: デバッグ用に文字列化して表示
function UserProfile({ user }) {
return (
<div>
<h2>ユーザー情報</h2>
<pre>{JSON.stringify(user, null, 2)}</pre>
</div>
);
}
3. Cannot read property 'map' of undefined
配列にmap
を使おうとした時のエラーです。
配列だと思っていたものがundefined
だった場合に起こります。
// ❌ エラーが発生するコード
function UserList({ users }) {
return (
<ul>
{users.map(user => ( // usersがundefinedの場合エラー
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
解決方法その1:条件分岐で確認
// ✅ 解決方法1: データがあるかチェック
function UserList({ users }) {
if (!users) {
return <div>ユーザーリストを読み込み中...</div>;
}
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
解決方法その2:デフォルト値で空配列を設定
// ✅ 解決方法2: 初期値として空の配列を設定
function UserList({ users = [] }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
解決方法その3:配列かどうかチェック
// ✅ 解決方法3: 本当に配列かどうか確認
function UserList({ users }) {
if (!Array.isArray(users)) {
return <div>ユーザーリストを読み込み中...</div>;
}
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
4. Warning: Each child should have a unique "key" prop
リストをレンダリングする時のkey警告です。 エラーではありませんが、修正した方が良い警告です。
// ❌ 警告が発生するコード
function UserList({ users }) {
return (
<ul>
{users.map(user => (
<li>{user.name}</li> // keyが不足
))}
</ul>
);
}
解決方法その1:一意のIDをkeyに使用
// ✅ 解決方法1: IDをkeyとして使用
function UserList({ users }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
解決方法その2:indexを使用(最後の手段)
// ✅ 解決方法2: 一意のIDがない場合はindexを使用
function UserList({ users }) {
return (
<ul>
{users.map((user, index) => (
<li key={index}>{user.name}</li>
))}
</ul>
);
}
5. Cannot update a component while rendering
レンダリング中に状態を更新しようとした時のエラーです。
// ❌ エラーが発生するコード
function App() {
const [count, setCount] = useState(0);
// レンダリング中に状態を更新(これがエラーの原因)
if (count > 5) {
setCount(0); // ここでエラー発生
}
return (
<div>
<p>カウント: {count}</p>
<button onClick={() => setCount(count + 1)}>
増加
</button>
</div>
);
}
解決方法その1:useEffectを使用
// ✅ 解決方法1: useEffectで状態を更新
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
if (count > 5) {
setCount(0);
}
}, [count]);
return (
<div>
<p>カウント: {count}</p>
<button onClick={() => setCount(count + 1)}>
増加
</button>
</div>
);
}
解決方法その2:イベントハンドラーで処理
// ✅ 解決方法2: ボタンクリック時に条件をチェック
function App() {
const [count, setCount] = useState(0);
const handleIncrement = () => {
const newCount = count + 1;
setCount(newCount > 5 ? 0 : newCount);
};
return (
<div>
<p>カウント: {count}</p>
<button onClick={handleIncrement}>
増加
</button>
</div>
);
}
効果的なデバッグテクニック
エラーの原因を見つけるための実践的な方法をご紹介します。
1. console.logを活用する
一番シンプルで効果的な方法です。 データの中身を確認してみましょう。
function UserProfile({ user }) {
// デバッグ用のログ出力
console.log('UserProfile に渡された user:', user);
// 条件分岐でのデバッグ
if (!user) {
console.log('user が undefined または null です');
return <div>ユーザー情報を読み込み中...</div>;
}
console.log('user.name:', user.name);
console.log('user.email:', user.email);
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
ブラウザの開発者ツールのコンソールタブで、ログの内容を確認できます。 データの中身が期待通りかどうか、一目で分かりますよ。
2. React Developer Toolsを使う
ブラウザの拡張機能です。 コンポーネントのpropsやstateをリアルタイムで確認できます。
function App() {
const [user, setUser] = useState(null);
useEffect(() => {
// API呼び出しの例
fetchUser().then(userData => {
console.log('取得したユーザーデータ:', userData);
setUser(userData);
});
}, []);
return (
<div>
{/* React Developer Tools で props と state を確認 */}
<UserProfile user={user} />
</div>
);
}
Chrome拡張機能で「React Developer Tools」をインストールすると、開発者ツールに「Components」タブが追加されます。 ここでpropsやstateの値をリアルタイムで確認できるんです。
3. エラーバウンダリーで安全に処理
エラーをキャッチして適切に表示する仕組みです。
// エラーバウンダリーコンポーネント
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('エラーをキャッチしました:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div>
<h2>申し訳ございません。エラーが発生しました。</h2>
<details>
<summary>エラーの詳細</summary>
<pre>{this.state.error?.toString()}</pre>
</details>
</div>
);
}
return this.props.children;
}
}
// 使用例
function App() {
return (
<ErrorBoundary>
<UserProfile user={user} />
</ErrorBoundary>
);
}
エラーバウンダリーを使うと、エラーが発生してもアプリ全体がクラッシュしません。 ユーザーにやさしいエラー表示ができますね。
4. 段階的にデバッグする
少しずつ複雑さを加えていく方法です。
// 段階的にコンポーネントをシンプルにしてデバッグ
function UserProfile({ user }) {
// 段階1: とりあえず表示できるかテスト
return (
<div>
<h2>ユーザープロフィール</h2>
<p>テスト表示</p>
</div>
);
// 段階2: props が渡されているかチェック
return (
<div>
<h2>ユーザープロフィール</h2>
<p>user: {user ? 'データあり' : 'データなし'}</p>
</div>
);
// 段階3: 実際の値を安全に表示
return (
<div>
<h2>ユーザープロフィール</h2>
<p>名前: {user?.name || '未設定'}</p>
<p>メール: {user?.email || '未設定'}</p>
</div>
);
}
一度にすべてを実装せず、少しずつ確認していくと問題の箇所を特定しやすくなります。
TypeScriptでエラーを予防しよう
TypeScriptを使うと、多くのエラーを事前に防げます。
基本的な型定義
// TypeScriptでの型定義
interface User {
id: number;
name: string;
email: string;
}
interface UserProfileProps {
user?: User; // オプショナルプロパティ(必須ではない)
}
function UserProfile({ user }: UserProfileProps) {
if (!user) {
return <div>ユーザー情報を読み込み中...</div>;
}
return (
<div>
<h2>{user.name}</h2> {/* 型が保証されているため安全 */}
<p>{user.email}</p>
</div>
);
}
型定義をすることで、存在しないプロパティにアクセスしようとした時にエラーで教えてくれます。 コードを書いている段階で問題に気づけるので、とても便利です。
配列の型定義
interface UserListProps {
users: User[]; // 必須の配列
}
function UserList({ users }: UserListProps) {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
配列であることが型で保証されているので、map
を使う時も安心です。
イベントハンドラーの型定義
interface ButtonProps {
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
children: React.ReactNode;
}
function Button({ onClick, children }: ButtonProps) {
return (
<button onClick={onClick}>
{children}
</button>
);
}
イベントハンドラーの型も定義しておくと、間違った使い方を防げます。
デバッグ環境を整えよう
効率的にデバッグするための環境設定をご紹介します。
開発用のデバッグ設定
// .env.development ファイル
REACT_APP_DEBUG=true
REACT_APP_LOG_LEVEL=debug
// デバッグ用のヘルパー関数
const debug = (message, data) => {
if (process.env.REACT_APP_DEBUG === 'true') {
console.log(`[DEBUG] ${message}:`, data);
}
};
// 使用例
function UserProfile({ user }) {
debug('UserProfile がレンダリングされました', { user });
return (
<div>
<h2>{user?.name}</h2>
<p>{user?.email}</p>
</div>
);
}
開発環境でのみデバッグ情報を表示する設定です。 本番環境では表示されないので安心ですね。
ESLintで問題を事前に発見
// .eslintrc.json
{
"extends": [
"react-app",
"react-app/jest"
],
"rules": {
"no-console": "warn",
"no-unused-vars": "error"
}
}
ESLintを設定しておくと、使っていない変数やconsole.logの書き忘れなどを教えてくれます。
まとめ
Reactでエラーが発生した時の対処法について、詳しく解説しました。
まず覚えておきたいポイント
- エラーメッセージは怖くない、情報の宝庫です
console.log
でデータの中身を確認する習慣をつけようundefined
チェックは基本中の基本- TypeScriptを使うとエラーを事前に防げる
デバッグの基本的な流れ
- エラーメッセージを読んで場所を特定
console.log
でデータの中身を確認- 条件分岐やデフォルト値で安全に処理
- React Developer Toolsで状態を確認
エラーは最初は怖く感じるかもしれませんが、実は「ここを直してくださいね」というReactからのメッセージなんです。 慣れてくると、エラーメッセージを見ただけで「あ、これはあれが原因だな」と分かるようになります。
ぜひ今回紹介した方法を試してみて、エラーを解決する楽しさを体験してみてくださいね。 きっとReact開発がもっと楽しくなりますよ!