コンソールに警告が溢れる理由
React アプリをバージョン 15 または 16 から 17 または 18 にアップグレードすると、多くの場合、コンソールに大量の警告が表示されます。これらのメッセージは、React が古いライフサイクルメソッドを段階的に廃止していることを示しています。具体的には、componentWillMount、componentWillReceiveProps、componentWillUpdate が廃止の対象となっています。
React は、これまで「will」で始まっていたメソッドが信頼できないものとなる「コンカレント(並行)レンダリングモデル」へと移行しています。これらのメソッドは、コンポーネントが実際にレンダリングされる前に複数回実行される可能性があり、予期しないサイドエフェクト(副作用)やメモリリークを引き起こす原因となります。アプリの安定性を維持するために、React は現在、UNSAFE_ プレフィックスを付けるか、完全なリファクタリングを行うことを求めています。
原因の特定
最初のタスクは、警告がどこから発生しているかを突き止めることです。自分のコードに原因がある場合、通常コンソールにはコンポーネントの特定の行が示されます。しかし、警告が node_modules フォルダから発生している場合、スタックトレースはミニファイ(最適化)されたコードの塊のように見え、判別が難しくなります。
推測に頼る必要はありません。VS Code のグローバル検索(Cmd+Shift+F)を使用して、文字列 componentWillMount() を検索してください。検索結果が src フォルダ内にある場合は、修正作業が必要です。もし結果が react-router v3 や material-ui v0.x のようなサードパーティ製ライブラリ内のみであれば、それらの依存関係をモダンなバージョンにアップデートすることを優先すべきです。
解決策 1:30秒でできる修正(UNSAFE_ プレフィックス)
大規模なコードベースを管理しており、すぐに警告を消す必要がある場合は、このアプローチを採用してください。これは「応急処置」的な修正です。コンポーネントの実際の動作を変えることなく、React の新しい命名規則を満たすことができます。
// 変更前:非推奨の方法
class LegacyComponent extends React.Component {
componentWillMount() {
console.log('Component is about to mount');
}
}
// 変更後:「安全な」非推奨の方法
class LegacyComponent extends React.Component {
UNSAFE_componentWillMount() {
console.log('Component is about to mount');
}
}
注意点として、これは警告を消すだけであり、そもそもそのメソッドが危険であるとされる根本的なアーキテクチャ上の問題を解決するものではありません。
解決策 2:react-codemod による一括更新
手動で数十個のライフサイクルメソッドの名前を変更するのは退屈な作業です。React チームは、この重労働を代行する codemod ツールを用意しています。以下のコマンドを実行することで、src ディレクトリ全体を数秒で更新できます。
npx react-codemod rename-unsafe-lifecycles
このスクリプトはファイルをスキャンし、すべての非推奨メソッドの先頭に自動的に UNSAFE_ を付与します。実行する前には必ず現在の進捗をコミットしておいてください。これにより、本番環境にプッシュする前に差分(diff)をレビューしやすくなります。
解決策 3:モダンなクラスパターンへのリファクタリング
最も賢明な方法は、componentWillMount を完全に排除することです。そこに記述されているロジックのほとんどは、実際には constructor または componentDidMount に記述されるべきものです。
シナリオ A:ステートの初期化
props に基づいてステートを設定するために componentWillMount を使用していた場合は、そのロジックを constructor に移動します。これにより、最初のレンダリングが発生する前にステートが準備されていることが保証されます。
// 非推奨
componentWillMount() {
this.setState({ user: this.props.defaultUser });
}
// 推奨
constructor(props) {
super(props);
this.state = {
user: props.defaultUser
};
}
シナリオ B:API コールとサブスクリプション
データの取得やイベントリスナーの設定はすべて componentDidMount に移動してください。componentWillMount は最初のレンダリングの前に実行されますが、componentDidMount はその直後に実行されます。99% のケースにおいて、このタイミングの差はユーザーには分からず、サーバーサイドレンダリング(SSR)においても大幅に安全です。
// 非推奨
componentWillMount() {
axios.get('/api/data').then(res => this.setState({ data: res.data }));
}
// 推奨
componentDidMount() {
axios.get('/api/data').then(res => this.setState({ data: res.data }));
}
解決策 4:ベストプラクティス(Hooks)
すでにリファクタリングを行っているのであれば、クラスを関数コンポーネントに変換しましょう。これが最も将来性のある方法です。Hooks には Will Mount に直接相当するものはありません。なぜなら、関数本体そのものがそのフェーズを処理するからです。
import React, { useState, useEffect } from 'react';
const MyComponent = ({ defaultUser }) => {
// ステートの初期化(コンストラクタの代わり)
const [user] = useState(defaultUser);
const [data, setData] = useState(null);
useEffect(() => {
// componentDidMountの代わり
const fetchData = async () => {
const res = await axios.get('/api/data');
setData(res.data);
};
fetchData();
}, []); // 空の配列により、この処理は1回のみ実行されます
return <div>{user.name}</div>;
};
確認チェックリスト
変更を適用した後、以下の項目を確認してください。
- アプリをリロードし、
Warning: componentWillMount has been renamedというメッセージが消えたことを確認する。 - DevTools の Network タブを開き、API コールが予期せず 2 回実行されていないか確認する。
- 空のステートによる「フラッシュ(ちらつき)」に注意する。ロジックを
componentDidMountに移動したことでちらつきが発生する場合は、その間を埋めるためのシンプルなローディングスピナーを追加する。
まとめ
- レンダリング前のサイドエフェクトを止める: コンポーネントがマウントされる前にステートを変更したり、外部アクションを実行したりしないでください。
- エコシステムを更新する: この警告は、古いバージョンの
react-routerやmaterial-uiに起因することがよくあります。依存関係を最新に保つことが解決への近道です。 - Hooks が未来: React はクラスのライフサイクルから離れつつあります。コンカレントモードや Suspense などの新機能は、特に関数コンポーネント向けに設計されています。

