Reactでエラーが出た時の対処法|初心者向けデバッグ術

React開発でよくあるエラーの原因と解決方法を初心者向けに解説。エラーメッセージの読み方からデバッグテクニックまで、実践的な対処法を紹介

Learning Next 運営
22 分で読めます

みなさん、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列目

簡単に言うと、「undefinednameプロパティを読もうとしてエラーになった」ということです。

実際のコードで確認してみよう

エラーメッセージと実際のコードを見比べてみましょう:

// 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

これは一番よく見るエラーです。 オブジェクトがundefinednullなのに、そのプロパティにアクセスしようとすると発生します。

// ❌ エラーが発生するコード
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を使うとエラーを事前に防げる

デバッグの基本的な流れ

  1. エラーメッセージを読んで場所を特定
  2. console.logでデータの中身を確認
  3. 条件分岐やデフォルト値で安全に処理
  4. React Developer Toolsで状態を確認

エラーは最初は怖く感じるかもしれませんが、実は「ここを直してくださいね」というReactからのメッセージなんです。 慣れてくると、エラーメッセージを見ただけで「あ、これはあれが原因だな」と分かるようになります。

ぜひ今回紹介した方法を試してみて、エラーを解決する楽しさを体験してみてくださいね。 きっとReact開発がもっと楽しくなりますよ!

関連記事