Reactの「Cannot update a component from inside the function body of a different component」を修正する

intermediate⚛️ React2026-03-19| React 16.13+、React 17、React 18 — OS不問、任意のバンドラー(Vite、CRA、Next.js)

Error Message

Cannot update a component from inside the function body of a different component.
#react#state-update#lifecycle#side-effect

このエラーが発生する原因ブラウザのコンソールを開くと、次のエラーが表示されます:

Warning: Cannot update a component from inside the function body of a different component.

Reactは、コンポーネントAのレンダーフェーズ中に、コンポーネントBの状態更新がトリガーされたことを検出しました。レンダーは純粋でなければなりません。副作用や状態の変更は許可されていません。レンダー中のコンポーネント間の状態更新は禁止されています。 ほとんどのケースで見られる3つのパターンがあります:

  • 子コンポーネントがレンダーボディ内で、propsとして渡された親の状態セッターを直接呼び出している- レンダー中にReduxまたはContextのアクションがディスパッチされている- useEffectの外で、レンダー中に同期的にコールバックを発火させるカスタムフック## バグを再現するこの最小限の例で、確実に警告を再現できます:
// Parent.jsx
function Parent() {
  const [status, setStatus] = useState('idle');

  return (
    <div>
      <p>Status: {status}</p>
      <Child onReady={() => setStatus('ready')} />
    </div>
  );
}

// Child.jsx — 壊れたコード
function Child({ onReady }) {
  onReady(); // <-- レンダー中に呼び出され、Parentの状態を更新する
  return <div>Child content</div>
}

Childがレンダーされると、即座にonReady()を呼び出します。その呼び出しがParent内のsetStatusを実行します。Reactはこれを検知して警告を発します。

クイック修正:呼び出しをuseEffectに移動する呼び出しをuseEffectでラップします。これはレンダーに実行され、レンダー中には実行されません:

// Child.jsx — 修正済み
import { useEffect } from 'react';

function Child({ onReady }) {
  useEffect(() => {
    onReady();
  }, []); // 最初のレンダー後に一度だけ実行

  return <div>Child content</div>
}

副作用がレンダーフェーズの外に移動しました。onReadyはマウント後に発火します。これが本来あるべき場所です。 依存配列に注意してください。 onReadyが親のレンダーごとに再生成される場合、このエフェクトはループで実行されます。親でuseCallbackを使って参照を安定させましょう:

// Parent.jsx
const handleReady = useCallback(() => {
  setStatus('ready');
}, []);

<Child onReady={handleReady} />

レンダー中のRedux / Contextディスパッチの修正コンポーネントボディ内でのディスパッチも同じ間違いであり、構文が異なるだけです:

// 壊れたコード
function NotificationBadge({ hasNew }) {
  if (hasNew) {
    dispatch(markAsSeen()); // <-- レンダー中に実行!
  }
  return <span>{hasNew ? 'New' : ''}</span>
}

// 修正済み
function NotificationBadge({ hasNew }) {
  useEffect(() => {
    if (hasNew) {
      dispatch(markAsSeen());
    }
  }, [hasNew, dispatch]);

  return <span>{hasNew ? 'New' : ''}</span>
}

レンダー中に親へ通知するコールバックの修正より微妙なパターン:子コンポーネントがレンダー中にpropのコールバックを条件付きで呼び出し、内部の状態を親に通知しようとするケースです。一見問題なさそうに見えますが、そうではありません:

// 壊れたコード — Dropdownのレンダー中にコールバックが親の状態を更新する
function Dropdown({ isOpen, onStateChange }) {
  if (isOpen) {
    onStateChange('open'); // 別のコンポーネントの状態更新をトリガーする
  }
  return <div>...</div>
}

// 修正済み
function Dropdown({ isOpen, onStateChange }) {
  useEffect(() => {
    onStateChange(isOpen ? 'open' : 'closed');
  }, [isOpen, onStateChange]);
  return <div>...</div>
}

知っておくべきこと:レンダー中にコンポーネント自身の状態を更新するのは別の話です。ガード条件を使えば、Reactは明示的にこれを許可しています:

// OK — ガード条件付きで自身の状態を更新する(getDerivedStateFromPropsのフック版)
function Dropdown({ isOpen }) {
  const [open, setOpen] = useState(false);
  if (isOpen !== open) {
    setOpen(isOpen); // Reactは即座に再レンダーし、中間コミットをスキップする
  }
  return <div>...</div>
}

Reactは中間状態をコミットせずに、コンポーネントを即座に再レンダーします。レンダー中に禁止されているのは、他のコンポーネントの状態を更新することだけです。

レンダー中にコールバックを発火するカスタムフックの修正カスタムフックでも同じ問題が隠れていることがあります。フックがuseEffectの外で、フックボディ内でコールバックを呼び出すと、同じ警告が発生します。ただし、発見するのが難しくなります:

// 壊れたカスタムフック
function useDataLoader(onLoad) {
  const data = fetchFromCache();
  if (data) {
    onLoad(data); // レンダー中に発火!
  }
  return data;
}

// 修正済みカスタムフック
function useDataLoader(onLoad) {
  const data = fetchFromCache();
  useEffect(() => {
    if (data) {
      onLoad(data);
    }
  }, [data, onLoad]);
  return data;
}

修正を確認する- ブラウザのDevToolsを開き、Consoleタブを選択します。- ページをリロードして、警告を引き起こしたアクションを実行します。- Cannot update a component...の警告が消えていることを確認します。- コンポーネントが正しく動作していることを確認します。状態の更新は引き続き行われ、レンダー中ではなくレンダー後に実行されるようになります。- React DevTools Profilerを使用している場合は、修正後のフレームグラフで予期しない再レンダーのループが発生していないか確認します。## まとめ- レンダーフェーズは純粋でなければなりません。他のコンポーネントに影響する状態更新は行わないでください。- コンポーネント間の状態更新はすべて、正しい依存配列を持つuseEffectに移動してください。- 参照を安定させるために、親でpropコールバックをuseCallbackでラップしてください。- Redux/Context:レンダー中にディスパッチしないでください。エフェクトやイベントハンドラーを使用してください。- ガード条件を使えば、レンダー中にコンポーネント自身の状態を更新することは許可されています。他のコンポーネントの状態を更新することは許可されていません。

Related Error Notes