開発者が以前よりはるかに少ないuseEffectを書いている理由

開発者が以前よりはるかに少ないuseEffectを書いている理由

かつてはすべての答えのように感じられたフックに代わるモダンなReactのパターン

すべてを解決したフック(実際にはそうではなかった)

初めてReactを学ぶとき、 useEffect は魔法のように感じます。状態を同期する必要がありますか?そのためのフックがあります。データをフェッチしたいですか?フック。2つのpropsを1つに組み合わせる?別のフック。いくつかのチュートリアルの後には、それが useEffect ほぼすべての問題の答えであるかのように感じ始めます。そして、より大きなものを構築します。

今日は2026年7月15日です。Reactコミュニティは、見直しを行っています。対象は useEffectです。大規模なアプリケーションに携わってきたシニア開発者であるAlejandroによる最近の記事によると、かつては不可欠だと感じられていたパターンが、初期のころに比べて彼が使用する頻度がおよそ80%も減少したものになっています。その理由は、 useEffect が悪いからではなく、開発者がそれで解決していた問題のほとんどに、はるかにシンプルな解決策があるからです。

これが今重要である理由は、Reactをうまく学ぶということは、次のことを学ぶことだと明らかになってきているからです。何を学ぶかというと、 いつ その最も有名なツールを使用すべきではないか、です。

useEffectが実際に構築された目的

公式の話から始めましょう。Reactのドキュメントでは、エフェクトを「コンポーネントを外部システムと同期する」方法として説明しています。これがキーワードです: 外部。ネットワークリクエスト、WebSocket接続、タイマー、ブラウザAPI、サブスクリプション、サードパーティライブラリなど、React自体の外側にあるものです。

エフェクトがReactの外側にあるものと通信していない場合、実際にはエフェクトを必要としない可能性が高いです。

あなたが間違っている可能性が高い5つのこと

エフェクト内で値を派生させる

最も一般的なパターンの1つは、 useEffect を使用してpropsやstateを新しい値に結合することです。例えば:

const [fullName, setFullName] = useState("");
useEffect(() => {
  setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);

これは機能しますが、Reactは2回レンダリングします。最初は元の空の状態で、エフェクトが実行され、状態が変更され、Reactが再度レンダリングします。その余分なレンダリングには理由がありません。

代わりに、レンダリング中に値を計算するだけです:

const fullName = `${firstName} ${lastName}`;

よりシンプルで、より速く、より少ないレンダリング。

propsをstateにコピーする

もう1つの一般的なパターンは、propをローカルstateに同期することです:

const [user, setUser] = useState(props.user);
useEffect(() => {
  setUser(props.user);
}, [props.user]);

これにより、2つの信頼できる情報源が作成されます。通常、一方が更新され、もう一方は更新されないことになります。ローカルの編集可能なコピーが特に必要ない限り(それはまれです)、propを直接使用してください。よりシンプルなアプローチは次のとおりです:

function Profile({ user }) {
  return <h2>{user.name}</h2>;
}

1つの信頼できる情報源。デバッグがはるかに簡単になります。

エフェクト内でリストをフィルタリングまたは変換する

エフェクト内でリストをフィルタリングし、結果をstateに保存するコードを見たことがあるでしょう:

const [filteredUsers, setFilteredUsers] = useState([]);
useEffect(() => {
  setFilteredUsers(users.filter(user => user.active));
}, [users]);

繰り返しになりますが、これは不要なstateです。レンダリング中に計算するだけです:

const filteredUsers = users.filter(user => user.active);

計算コストが高い場合は、そのためのフックがありますが、それは useEffectではありません。それは useMemoであり、依存関係が変更された場合にのみ再計算されるように結果をメモ化します:

const filteredUsers = useMemo(() => {
  return users.filter(user => user.active);
}, [users]);

しかし、覚えておいてください: useMemo最適化であり、コードについて考えることに代わるものではありません。

デバッグのためにエフェクトを使用する

一時的にエフェクトが意味を持つ場所が1つあります。それはデバッグです。値が変更されるたびにログを記録することは、純粋に役に立ちます:

useEffect(() => {
  console.log(user);
}, [user]);

しかし、これらはコードをマージする前に削除する必要があります。

昔ながらの方法でデータをフェッチする

数年前まで、ほぼすべてのReactプロジェクトにはこのパターンがありました:

useEffect(() => {
  fetch("/api/users")
    .then(res => res.json())
    .then(setUsers);
}, []);

それは機能しますが、多くは行いません。エラー処理、読み込み状態、再試行ロジック、コンポーネントが2回マウントされた場合の重複排除はありません。ほとんどのチームは、結局それらすべてを自分たちで構築することになりました。

現在では、より優れたオプションがあります。TanStack QueryやSWRのようなライブラリは、キャッシュ、再試行、バックグラウンドの再フェッチ、読み込み状態、エラー状態、重複排除を自動的に処理します。エフェクトを書く代わりに、フックを使用します:

const { data, isLoading } = useQuery({
  queryKey: ["users"],
  queryFn: getUsers
});

はるかに少ないコード。バグの数ははるかに少なくなります。より良い開発者体験。

エフェクトが本当の問題を隠すとき

Alejandroが時間をかけて気づいたパターンがあります。コンポーネントに多くのエフェクトがある場合、それは通常、やりすぎています。おそらく、データのフェッチ、フィルタリング、ソート、フォーマット、検証、イベントの処理すべてを1つのコンポーネント内で行っています。それはエフェクトの問題ではなく、アーキテクチャの問題です。

多くの場合、責任をより小さなフックやコンポーネントに分割することで、エフェクトの半分が自動的に削除されます。

実際にuseEffectが必要な場合

これは、「決して useEffectを使用しない」という意味ではありません。正当な理由はたくさんあります:

WebSocket接続: コンポーネントがマウントされたときに接続を開き、アンマウントされたときに閉じる必要があります。これはまさにエフェクトが目的とするものです。

タイマー: 5秒ごとに更新をポーリングする必要がある場合、 setInterval を適切なクリーンアップとともにエフェクト内で使用することは理にかなっています。

ブラウザAPI: ウィンドウの resize イベントをリッスンしたり、 localStorage と同期したりすることは、副作用です。

サードパーティライブラリ: チャートライブラリや分析SDKの初期化 — これらはコンポーネントがマウントされたときに実行する必要があります。

これらは、エフェクトが設計されたまさにそのシナリオです。つまり、Reactの外部にある何かとの同期です。

すべてを変える質問

エフェクトを書く前に、Alejandroは自分自身に1つのことを問いかけます。「私は外部システムと同期しているのか、それともコンポーネント設計の埋め合わせをしているのか?」

その質問だけでも、プロジェクトから驚くほど多くの不要なコードが削除されたと彼は言います。

結論

useEffect は悪いものではありません。ただ、ほとんどのReactフックよりも使いすぎやすいだけです。現代のReact開発者は、派生値に手を伸ばし、propsをpropsとして維持し、サーバーの状態にはクエリライブラリを使用し、実際に必要なもののためにエフェクトを取っておく傾向があります。その結果、レンダリングが減り、管理する状態が減り、バグが減り、数か月後にはるかに理解しやすいコンポーネントになります。

メリット

  • 不要な再レンダリングを減らし、パフォーマンスを向上させる
  • 冗長な状態やエフェクトを避けることでコードを簡素化する
  • 副作用が少なく、コンポーネントのデバッグと保守が容易になる
  • クエリライブラリが複雑なデータフェッチロジックを自動的に処理する
  • エフェクトが設計上の問題を浮き彫りにするとき、より良いコンポーネントアーキテクチャになる
  • 状態が少ないということは、バグが潜む場所が少ないということである

デメリット

  • 代替手段(useMemo、カスタムフック、クエリライブラリ)をいつ使用するかを学ぶ必要がある
  • エフェクトを多用するパターンに慣れている開発者は、習慣を変える必要があるかもしれない
  • 一部のレガシープロジェクトはuseEffectパターンに大きく依存しており、一晩でリファクタリングすることはできない
  • すべてのチームがまだTanStack Queryのようなライブラリを採用しているわけではない
  • インライン計算は、慎重に行わないと可読性が低下する可能性がある

注意

この記事は教育的なものであり、コミュニティの記事で議論されている現代のReactパターンについて説明しています。コード例は説明のみを目的としています。実際のプロジェクトで使用する場合は、プレースホルダーの値を実際のAPIエンドポイントとロジックに置き換えてください。本番環境のコードでそれらに依存する前に、必ず元のソースと照らし合わせてパターンを確認してください。Reactとエコシステムは急速に進化しています。最新のガイダンスについては、公式のReactドキュメントとライブラリのドキュメント(TanStack Query、SWR)を確認してください。

よくある質問

  • 状態を派生させる代わりにuseEffectを使用すべきなのはいつですか? — 使用してください、 useEffect 外部システム(API、タイマー、ブラウザイベント)と同期している場合にのみ。既存のデータを単に変換しているだけの場合は、レンダリング中に派生させます。

  • 計算にuseEffectよりもuseMemoの方が適していますか?useMemo は高コストな計算を最適化しますが、慎重に使用してください。ほとんどの計算は毎回のレンダリングで計算しても十分に速いです。プロファイリングによってそれが重要であることが示された場合にのみ、メモ化してください。

  • useEffectのデータフェッチをすべて置き換えるべきですか? — TanStack Queryのようなライブラリは、はるかに強力です。 useEffectよりも。しかし、大規模なコードベースの移行には時間がかかります。新機能から始めて、徐々にリファクタリングしてください。

  • TanStack QueryとSWRの違いは何ですか? — どちらもキャッシュと再フェッチを処理するクエリライブラリです。TanStack Queryはより機能が豊富で、SWRはよりシンプルで軽量です。プロジェクトのニーズに基づいて選択してください。

  • デバッグのために引き続きuseEffectを使用できますか? — はい。ただし、コードをマージする前にデバッグエフェクトを削除してください。本番環境のデバッグには、代わりにブラウザのDevToolsを使用してください。

  • コンポーネントがやりすぎているかどうかはどうすればわかりますか? — 2つまたは3つ以上のエフェクトがある場合、またはエフェクトが多くの異なるものに依存している場合は、より小さなコンポーネントまたはカスタムフックに分割することを検討してください。

  • useEffectを避けることで、Reactを学ぶのが難しくなりますか? — そうではありません。それはReactのコアモデルをよりよく学ぶことを意味します。いつ 〜しないか 機能を使用すべきかを理解することで、それが実際に何のためのものかが明確になることがよくあります。

  • WebSocketやブラウザAPIはどうですか?それらは常にuseEffectを必要としますか? — はい、Reactの外部の何かのライフサイクルを管理している場合、 useEffect を適切なクリーンアップとともに使用することが、正しいツールです。

タグ

#react #useeffect #javascript #webdev #frontend #reacthooks #modernreact #bestpractices

Free field guide

Incident Response: First Hour

A calm, evidence-preserving checklist for establishing control, bounding impact, communicating clearly, and containing an incident safely.