【入門編】 useCallbackとuseEffectの連携 – React実践ガイド

みなさん、こんにちは!Reactの世界へようこそ。

Reactを学び始めて、画面が動くようになると本当にワクワクしますよね。でも、開発を進めていくうちに、こんな不思議な現象に出会ったことはありませんか?

「画面が何度もチカチカして、処理が止まらなくなっちゃった!」
「`useEffect`の依存配列(右下の `[]` の中身)に黄色い波線が出て、『関数を入れなさい』って怒られる……。でも、入れると無限ループになりそうで怖い!」

大丈夫、安心してください。これはReactを学ぶ誰もが、本当に100人中100人が一度は通る「お約束の壁」です。決してあなたの理解が足りないわけではありません。

今回は、この問題の救世主となる`useCallback`と`useEffect`の連携パターンについて、専門用語をできるだけ使わず、身近なお買い物の例え話を使って、世界一優しく解きほぐしていきます。

この記事を読み終える頃には、「あぁ、そういうことか!」とスッキリ納得して、自信を持ってコードが書けるようになりますよ。一緒に一歩ずつ進んでいきましょう!

—

1. なぜ「関数」をuseEffectに入れると暴走するの?

まずは、身近な例え話から始めてみましょう。

あなたのお家に、代わりに買い物に行ってくれる「お買い物ロボット(useEffect)」がいると想像してください。

あなたはロボットに、「このお買い物メモ(関数)に書いてあるものを買ってきてね」とお願いしました。
ロボットはとても真面目なので、「メモの内容が変わったときだけ、スーパーに買い出しに行く」というルールで動いています。これが、`useEffect`の「依存配列」の仕組みです。

ここで、Reactのちょっとした「お茶目なクセ」が登場します。

Reactは、画面のパーツ(コンポーネント)が新しく書き換わる(再レンダリングされる)たびに、「お買い物メモ」を全く同じ内容で新しく紙に書き直して、あなたに手渡すのです。

  • 1回目の買い物: 「牛乳」と書かれたメモを渡す。ロボットはスーパーへ行く。
  • 画面が更新される: あなたは、全く同じ「牛乳」という文字を書いた新しいメモ用紙をロボットに渡す。
  • ロボットの反応: 「あ、新しいメモ用紙だ!前の紙とは別物だから、内容が同じでも、もう一度スーパーに行かなきゃ!」

ロボット(useEffect)からすると、「紙(関数の実体)が新しくなった=新しい命令が出た」と勘違いしてしまうのです。その結果、ロボットは休む間もなくスーパーと家を往復し続けてしまいます。これが「無限ループ(暴走)」の正体です。

—

2. 救世主 `useCallback` の登場!

このロボットの勘違いを防ぐために登場するのが、`useCallback`(ユーエス・コールバック)です。

`useCallback`を一言でいうと、「お買い物メモをラミネート加工して、ずっと使い回す技術」です。

「このメモ(関数)は、中に書いた条件(依存配列)が変わらない限り、新しく書き直さずに同じラミネートカードをずっと使い回してね!」とReactに指示を出します。

そうすれば、画面が何度書き換わっても、ロボットに渡されるのは「まったく同じラミネートカード」。ロボットも「あ、さっきと同じカードだから、買い物に行かなくていいや」と賢く判断してくれるようになります。

—

3. 実際のコードで見てみよう!

では、実際にエディタに貼り付けて動かせるコードを見てみましょう。
今回は「ボタンを押すと、お買い物リスト(データの取得)をやり直す」というシンプルなアプリを作ってみました。

コードの中のコメントで、何が起きているかを優しく解説しています。

import React, { useState, useEffect, useCallback } from ‘react’;

function ShoppingApp() {
const [category, setCategory] = useState(‘野菜’);
const [count, setCount] = useState(0); // 買い物の回数カウント

// ==========================================
// ★ ここがポイント! useCallbackで関数を包みます
// ==========================================
// これにより、「category」が変わらない限り、
// fetchItems という関数(メモ用紙)は新しく作り直されず、再利用されます。
const fetchItems = useCallback(() => {
console.log(`📡 【API通信】「${category}」のデータをインターネットから取得しました!`);
}, [category]); // ← 「category」が変わった時だけ、関数を新しく作り直します

// ==========================================
// ★ useEffect の中で、上で作った関数を呼び出します
// ==========================================
useEffect(() => {
// 1. 関数を実行してお買い物データを取ってくる
fetchItems();

console.log(‘🤖 [useEffect] 関数が実行されたので、お買い物データを更新しました。’);

}, [fetchItems]); // ← 安全に「fetchItems」を依存配列に入れることができます!

return (

🛒 お買い物マネージャー

現在のカテゴリ: {category}

{/ カテゴリを変えると、fetchItemsが作り直され、useEffectが動きます /}


{/
このボタンを押すと「count」が増えて、画面が再レンダリング(再描画)されます。
しかし、fetchItemsは「useCallback」で守られているため、
画面が再描画されても関数は作り直されず、余計なAPI通信(暴走)は起きません!
/}

ただのカウンター: {count}

);
}

export default ShoppingApp;

このコードの「ここがすごい!」

1. 「カウントを増やす」ボタンを押したとき:
画面は新しく描き直されますが、`category` は変わっていません。そのため、`useCallback` は以前と同じ「ラミネートされた関数」をそのまま返します。`useEffect` は「何も変わっていないな」と判断し、余計な通信を一切行いません。
2. 「カテゴリを『果物』にする」ボタンを押したとき:
`category` が変わったので、`useCallback` は新しいメモ(果物用の関数)を作ります。`useEffect` も「メモが変わった!」と気づいて、正しく新しいデータを取得しに行きます。

見事に、無駄な動きを完璧にコントロールできていますね!

—

4. つまずきやすいポイントへの「そっとフォロー」

ここで、もしかしたらこんな疑問が浮かんだかもしれません。

「そもそも、なんで関数を依存配列(`[]`)に入れなきゃいけないの?」

「入れなければ、無限ループも起きないし楽じゃない?」と思いますよね。
実は、Reactのルール(ESLintというコードの監視役)が「useEffectの中で使っている外の変数は、全部依存配列に入れなさい」と強く勧めてきます。

なぜなら、依存配列に入れないと、「古いデータを持ったままの幽霊のような関数」をずっと使い続けてしまい、画面の表示がバグってしまう原因になるからです。

Reactは常に、「今の最新の状態」を正しく画面に映し出したい、とても真っ直ぐで正直な仕組みを持っています。だからこそ、
1. `useEffect` の中で使う関数は、ちゃんと依存配列に入れる。
2. そのままだと暴走しちゃうから、`useCallback` でラミネートしてあげる。

この2つのセットが、Reactの世界で美しく安全にコードを書くための王道のテンプレートになっているのです。

—

まとめ:今日のポイントをおさらい!

  • Reactは、画面が更新されるたびに関数を「新しく作り直す」クセがある。
  • `useEffect`の依存配列に関数を入れると、毎回「新しくなった!」と勘違いして暴走(無限ループ)しやすい。
  • `useCallback` は、関数をラミネート保存して「中身が変わらない限り使い回す」ための道具。
  • この2つを組み合わせることで、余計な通信やバグのない、軽快でプロフェッショナルなアプリが作れる!

最初は「なんだか難しそうだな…」と感じるかもしれませんが、何度もコードを書いて動かしていくうちに、「あ、ここでラミネート(useCallback)の出番だ!」と直感的にわかる瞬間が必ずやってきます。

焦らず、自分のペースで、この可愛いロボット(React)との付き合い方を楽しんでいってくださいね。あなたのReact開発を心から応援しています!

コメント

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