【入門編】 副作用をカプセル化するカスタムフック – React実践ガイド

Reactの「useEffect」が驚くほどスッキリ!「カスタムフック」で裏方仕事をスマートに隠す魔法

Reactを学び始めて、画面が動く楽しさを知ったあなたへ。本当に素晴らしい一歩を踏み出しましたね!

でも、APIからデータを取ってきたり、タイマーを使ったりする「副作用(`useEffect`)」を書き始めた途端、こんな風に悩んでいませんか?

「画面を表示したいだけなのに、コードが `useEffect` だらけで何が書いてあるか分からない……」
「依存配列(`[]`)ってやつが難しくて、何度も無限ループを起こして画面が固まっちゃう!」

大丈夫、全く心配いりません。現場で何年もコードを書いているプロのエンジニアでも、最初はみんな同じ道を通って、同じように頭を抱えてきました。

この記事では、そんなお悩みを一瞬で解決する「副作用をカプセル化するカスタムフック」という素晴らしいテクニックをご紹介します。

難しい専門用語は、身近な例え話に置き換えて、優しく丁寧に分解していきます。読み終わる頃には、「なんだ、そういうことか!」と心がすっと軽くなっているはずです。一緒に楽しく学んでいきましょう!

—

そもそも「副作用」と「カプセル化」ってなに?

まずは、ちょっと肩の力を抜いて、身の回りにある「道具」をイメージしてみてください。

1. 副作用(useEffect)は「裏で動く全自動お掃除ロボット」

Reactにおける「主なお仕事」は、画面にボタンやテキストを表示することです。
これに対して、「主なお仕事のついでに、裏でこっそり動くお仕事」のことを副作用と呼びます。

例えば、お買い物アプリで「商品一覧」を表示するのが主なお仕事なら、「裏でインターネット(API)におねだりして、最新の在庫データを取ってくること」が副作用です。
これはまるで、あなたがリビングでテレビを見ている(主なお仕事)間に、裏で静かに部屋をきれいにしてくれる「全自動お掃除ロボット」のような存在ですね。

2. カプセル化は「お掃除ロボットのカバー」

お掃除ロボットの中には、複雑な配線や、ゴミを吸い込むためのファン、AIの基盤などがギッシリ詰まっています。でも、私たちはそんな難しい中身を知らなくても、上の「スタートボタン」をポンと押すだけで使えますよね。

このように、「複雑な中身をカバー(カプセル)の中に隠して、外からは簡単なボタンひとつで動かせるようにすること」を、プログラミングの世界ではカプセル化と呼びます。

そして、Reactでこの「カバー」の役割を果たしてくれるのが、今回の主役である「カスタムフック」です!

—

なぜコンポーネントから「裏方仕事」を追い出すの?

もし、お掃除ロボットにカバーがなくて、むき出しの配線やモーターがリビングの真ん中に転がっていたらどうでしょう? 歩くたびに足に引っかかって危ないですし、何より部屋が見苦しくなってしまいますよね。

Reactのコンポーネントも全く同じです。
画面を作るコード(HTMLのような部分)のすぐ隣に、複雑な `useEffect` が何十行もむき出しで置いてあると、コードを読む人が「うわっ、ごちゃごちゃしていて読みたくないな……」と迷子になってしまいます。

そこで、「裏方の難しい仕事は、専門のプロ(カスタムフック)に全部お任せして、コンポーネントには画面を作る仕事だけに集中してもらおう!」という役割分担(責務の分離)を行います。

—

実践:お買い物の「在庫状況チェック」を作ってみよう!

それでは、実際によくある「商品の在庫状況をインターネットから取ってくる」という機能を例にして、コードを見てみましょう。

まずは、「ちょっとお部屋が散らかってしまっている状態(ビフォー)」のコードです。

❌ ビフォー:useEffectがコンポーネントの中に丸出しの状態

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

// 商品の在庫を表示するコンポーネント
export function ProductStockDisplay({ productId }) {
const [stock, setStock] = useState(null);
const [isLoading, setIsLoading] = useState(true);

// 🔴 コンポーネントの中に、むき出しの「裏方仕事(useEffect)」があります
useEffect(() => {
let isMounted = true; // クリーンアップ用のフラグ
setIsLoading(true);

// インターネットからデータを取ってくる(擬似的なAPI呼び出し)
fetch(`https://api.example.com/products/${productId}/stock`)
.then((res) => res.json())
.then((data) => {
if (isMounted) {
setStock(data.stock);
setIsLoading(false);
}
})
.catch((err) => {
console.error(“データ取得に失敗しました”, err);
if (isMounted) setIsLoading(false);
});

// 蛇口を閉めるような「お片付け(クリーンアップ)」の処理
return () => {
isMounted = false;
};
}, [productId]); // 商品IDが変わるたびに、お掃除ロボットが再始動します

// 画面を作る主なお仕事
if (isLoading) return

在庫を調べています…

;
return

現在の残り在庫:{stock ?? ‘売り切れ’} 個

;
}

どうでしょうか。画面に「在庫数」を表示したいだけなのに、データの取得(`fetch`)や、エラーが起きたときの処理、お片付け用の `isMounted` など、難しいコードが画面を作るコードとごちゃ混ぜになっていますね。

これでは、後からこのコードを読んだときに、どこからどこまでが画面の表示で、どこからが裏方仕事なのか見分けるのが大変です。

—

⭕ アフター:カスタムフックに「カプセル化」してスッキリ!

では、この難しい裏方仕事を、専用のカスタムフック `useProductStock` に引っ越しさせてみましょう。
カスタムフックの名前は、必ず頭に `use` をつけるのがReactのルールです。

まずは、裏方仕事だけを引き受ける「カスタムフック(専門家)」を作ります。

import { useState, useEffect } from ‘react’;

// 🎁 これが「カスタムフック」です!
// 複雑な裏方仕事(useEffect)を、この中に全部閉じ込め(カプセル化)ます。
export function useProductStock(productId) {
const [stock, setStock] = useState(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
let isMounted = true;
setIsLoading(true);

fetch(`https://api.example.com/products/${productId}/stock`)
.then((res) => res.json())
.then((data) => {
if (isMounted) {
setStock(data.stock);
setIsLoading(false);
}
})
.catch((err) => {
console.error(“データの取得に失敗しました”, err);
if (isMounted) setIsLoading(false);
});

// お片付け(クリーンアップ関数)
return () => {
isMounted = false;
};
}, [productId]); // 監視する対象をここにまとめます

// 使う人に渡したい「結果」だけを、お盆に乗せて返します
return { stock, isLoading };
}

どうですか? 難しい処理はすべてこのフックの中に閉じ込められました。

それでは、このカスタムフックを使って、先ほどのコンポーネント(画面を作る人)を書き直してみましょう!

import React from ‘react’;
// さっき作ったカスタムフックを読み込みます
import { useProductStock } from ‘./useProductStock’;

// ✨ こんなにスッキリしたコンポーネントになりました!
export function ProductStockDisplay({ productId }) {
// 専門家に「この商品の在庫を教えて!」と頼むだけ
const { stock, isLoading } = useProductStock(productId);

// コンポーネントは「画面を作るお仕事」に100%集中できます
if (isLoading) return

在庫を調べています…

;
return

現在の残り在庫:{stock ?? ‘売り切れ’} 個

;
}

驚くほどスッキリしましたよね!
コンポーネントのコードはたったの数行になり、「何を画面に表示したいのか」がひと目で分かるようになりました。

—

つまずきやすいポイントを優しくフォロー

カスタムフックを作る上で、初心者の皆さんが「うーん、ここがよく分からない……」となりがちなポイントを2つ、そっと解説しておきますね。

Q1. 「依存配列(最後の `[productId]`)」って結局なんなの?

依存配列は、「お掃除ロボット(useEffect)が、いつ活動を開始するかを決めるトリガー」です。

もしここを空っぽ(`[]`)にすると、「最初にお店に入ったとき(画面が表示されたとき)に1回だけ動く」という意味になります。
でも、お客さんが「別の商品」を選んだら、在庫状況も新しく調べ直さないといけませんよね。
だから、`[productId]` と書いておくことで、「商品IDが変わったタイミングで、もう一度最新の在庫を調べ直してね!」とReactに教えているのです。

Q2. 「クリーンアップ(`return` の中の関数)」って本当に必要なの?

これは、「使い終わったおもちゃを片付ける」「出しっぱなしの水道の蛇口を閉める」ような、とても大切なお片付け作業です。

例えば、インターネットからデータを取ってきている最中に、ユーザーがパッと別の画面に移動してしまうことがあります。
もしお片付けをしていないと、データが届いたときに「あれ? 届ける先の画面がもう消えているぞ!?」とReactがパニック(メモリリークというバグ)を起こしてしまいます。

カスタムフックの中に `return () => { isMounted = false; }` のようなお片付け処理を書いておくことで、画面が消える瞬間に「もうデータは届けなくていいよ」と安全にキャンセルすることができるのです。

この難しい「お片付けのルール」を、使う側(コンポーネント)が一切気にしなくていいように裏に隠せることこそが、カスタムフックの最大の優しさなのです。

—

まとめ:一歩ずつ、引き出しを整理していこう

今回は、複雑な `useEffect` をカスタムフックに閉じ込める「カプセル化」について解説しました。

最後に、大切なポイントをおさらいしましょう。

1. コンポーネントは「見た目(UI)」を作る主役。裏方仕事はさせない。
2. `useEffect` などの難しい裏方仕事は、カスタムフック(useから始まる関数)に外注する。
3. これによって、コードが劇的に読みやすくなり、他の場所でも使い回せる(再利用できる)ようになる!

プログラミングは、お部屋の片付けとよく似ています。
最初は一つの箱(コンポーネント)になんでもかんでも詰め込んでしまいがちですが、「これはお掃除の道具だから、お掃除用の引き出し(カスタムフック)にしまおう」と整理整頓していくだけで、見違えるほどきれいなコードになります。

一度に完璧にできなくても、全く問題ありません。「ちょっと `useEffect` が増えてきたな」と思ったら、この記事を思い出して、そっと新しい引き出しを作ってみてくださいね。

あなたのReact開発が、もっと楽しく、もっと心地よいものになりますように。応援しています!

コメント

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