【実務・中級編】 useEffectの過剰使用(Over-using)とリファクタリング – React実践ガイド

Reactの「useEffect」を捨てろ:副作用の沼から脱出するアーキテクチャ思考

現場でコードレビューをしていると、必ずと言っていいほど遭遇する光景があります。それが「useEffectの乱用」です。

「状態が変わったから、useEffectで別の状態を更新しよう」。一見、Reactのデータフローに従っているように見えますが、これこそがアプリケーションを複雑にし、パフォーマンスを低下させ、そして何より「なぜかバグが消えない」という負のループを生み出す元凶です。

今日は、なぜ`useEffect`が「最後の手段」であるべきなのか、そしてそれをどうやって「クリーンな計算」に置き換えるのか。実務レベルで差がつく考え方を伝授しましょう。

—

1. なぜ「同期のためのuseEffect」が罪深いのか

多くのエンジニアが陥る罠がこれです。

// ❌ アンチパターン:レンダリングのたびに無駄な同期が走る
const [firstName, setFirstName] = useState(‘Taro’);
const [lastName, setLastName] = useState(‘Yamada’);
const [fullName, setFullName] = useState(”);

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

これの何が問題か?「レンダリングが2回走る」からです。
1. Reactがコンポーネントをレンダリングし、DOMを更新する。
2. ブラウザが画面を描画する。
3. その後、`useEffect`が動き、`setFullName`が走り、再レンダリングが発生する。

ユーザーから見れば、一瞬だけ名前が空(あるいは古い状態)で表示され、ガクッと画面が更新される。いわゆる「レイアウトシフト」や「チカつき」の原因です。さらに、依存配列が複雑になればなるほど、無限ループの恐怖と戦うことになります。

2. 「レンダリング中に計算する」という原則

Reactの設計思想における鉄則は、「レンダリング時に計算できるものは、レンダリング中に計算する」です。

`fullName`は、`firstName`と`lastName`から導出できるデータですよね?であれば、わざわざ`state`として保持する必要はありません。

// ✅ ベストプラクティス:レンダリング中に直接計算する
const [firstName, setFirstName] = useState(‘Taro’);
const [lastName, setLastName] = useState(‘Yamada’);

// 導出された状態(Derived State)
const fullName = `${firstName} ${lastName}`;

これだけで、再レンダリングのコストはゼロになります。`fullName`はレンダリングのたびに、ただの文字列結合として計算されます。現代のJavaScriptエンジンにとって、この程度の計算は誤差です。

3. 計算コストが高い場合:useMemoの出番

「いやいや、計算負荷が高い処理ならどうするんだ?」という声が聞こえてきそうです。その場合は、素直に`useMemo`を使いましょう。

// ✅ 高負荷な計算は useMemo でメモ化する
const expensiveData = useMemo(() => {
return complexCalculation(items);
}, [items]); // items が変わった時だけ再計算

`useMemo`は「副作用」ではありません。「キャッシュ」です。副作用(API通信やDOM操作)を隠蔽するための道具ではなく、あくまで「計算結果をメモリに保持する」ための最適化ツールだと認識してください。

—

4. 現場で使えるリファクタリングの判断基準

「このuseEffect、本当に必要?」と迷ったときは、以下のフローチャートを思い出してください。

1. その処理は、propsやstateから「導出」可能か?

  • YES → 直接計算、または`useMemo`へ移行。

2. 外部システム(DOM操作、タイマー、API通信)との同期か?

  • YES → `useEffect`を使う正当な理由あり。

3. 副作用の結果を別のstateに保存しようとしているか?

  • YES → それは「同期の連鎖」を引き起こす設計ミスです。ロジックをコンポーネントの外へ出すか、計算で解決できないか再考を。

—

まとめ:魔法の杖としてのuseEffectは捨てる

`useEffect`は強力ですが、Reactにおける「最後の切り札」です。これを多用するコードは、まるで複雑に絡まり合ったスパゲッティのような依存関係を生みます。

「副作用を減らし、計算を増やす」

この意識を持つだけで、あなたの書くコンポーネントは劇的にシンプルになり、テストも書きやすく、何よりデバッグのストレスから解放されます。

次のPRで、`useEffect`の中に`setXXX`を見つけたら、まずは削除を検討してください。「これ、本当に必要?」と自問するその一歩が、シニアエンジニアへの第一歩です。

さあ、コードを綺麗にしましょう。現場からは以上です。

コメント

タイトルとURLをコピーしました