Reactのカスタムフックの作り方を解説!初心者でもわかる再利用可能なロジックの基本
生徒
「先生、Reactのカスタムフックって何ですか?自分でも作れるんですか?」
先生
「もちろんです!カスタムフックは、自分で作ることができる再利用可能な機能のことですよ。」
生徒
「再利用できるってどういう意味ですか?」
先生
「たとえば、複数のコンポーネントで同じ処理をしたいときに、その処理をひとつの関数としてまとめておけるんです。では、実際に作り方を見ていきましょう!」
1. カスタムフックとは?
React(リアクト)でアプリを作っていると、同じようなロジック(処理)を何度も書く場面がよく出てきます。 例えば、ボタンを押した回数を数える処理や、入力フォームの文字を保存する処理などです。 これらを毎回コンポーネントごとに書いていると、コードが増えて管理が大変になります。 そこで役立つのが「カスタムフック(Custom Hook)」です。
カスタムフックとは、useStateやuseEffectといったReactのフックを組み合わせて、
よく使う処理をひとつの関数にまとめたものです。
名前は必ず「use」から始めるというルールがあり、
そうすることでReactが「これはフックだ」と正しく判断してくれます。
初心者の方は「部品化された便利な処理」と考えると分かりやすいでしょう。 たとえば「ボタンを押したら数が増える」という単純な動きでも、 カスタムフックにしておくと、別の画面でも同じ動きを簡単に使い回せます。 これにより、コードがすっきりし、修正もしやすくなります。
import { useState } from "react";
// 数を数えるだけのシンプルなカスタムフック
function useSimpleCounter() {
const [count, setCount] = useState(0);
const add = () => setCount(count + 1);
return { count, add };
}
このように、カスタムフックは「よく使う処理をまとめて再利用するための仕組み」です。 難しく考えず、まずは「同じ処理を何度も書かなくてよくなる便利な方法」として覚えておくと、 これからのReact学習がぐっと楽になります。
2. カスタムフックを作る基本の書き方
では、Reactでカスタムフックを作る基本の形を見てみましょう。 カスタムフックは見た目は普通のJavaScript関数ですが、Reactで正しく動かすために守るべきルールがあります。 ここを押さえておくと、あとから別の画面でも同じ処理を再利用しやすくなり、コードの整理もしやすくなります。
- 関数名は「use」から始める(例:
useCounter) - フック(
useStateやuseEffectなど)を関数の中で使える
特に大事なのは「useから始める」ことです。名前がuseで始まっていないと、 Reactがフックとして扱えず、ルール違反としてエラーや予期しない動きにつながることがあります。 また、カスタムフックは「値を返す」ことで初めて便利になります。画面側(コンポーネント)で使いたい 状態(countなど)や、更新用の関数(incrementなど)をまとめて返すのが定番の形です。
まずは、シンプルな「カウンター機能」をカスタムフックとして作ってみましょう。 ここでは初期値を受け取れるようにして、増やす・減らす・元に戻す、というよくある操作をまとめています。 初心者の方は「カウンター一式をセットで用意する」と思うと理解しやすいです。
import { useState } from "react";
// カスタムフックの定義
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount((prevCount) => prevCount + 1);
const decrement = () => setCount((prevCount) => prevCount - 1);
const reset = () => setCount(initialValue);
// 外から使いたい値や関数を返す
return { count, increment, decrement, reset };
}
export default useCounter;
この例のポイントは、状態を更新するときに「前の値」を使える形にしていることです。
setCount((prevCount) => prevCount + 1)のように書くと、クリックが連続したときでも値がずれにくくなります。
カスタムフックは、こうした「よくある安全な書き方」をまとめておけるのが強みです。
あとは、コンポーネント側でこのフックを呼び出すだけで、同じロジックを何度でも使えるようになります。
3. カスタムフックを使ってみよう
それでは、作成したカスタムフックを実際のReactコンポーネントで使ってみましょう。
カスタムフックの使い方はとてもシンプルで、通常のuseStateやuseEffectと同じ感覚で呼び出せます。
「特別な書き方を覚えないといけない」ということはなく、関数として呼ぶだけで中の処理が動きます。
下の例では、先ほど作ったuseCounterというカスタムフックを読み込み、
カウントの値と操作用の関数をまとめて受け取っています。
画面側のコードは「表示」と「ボタンの配置」だけに集中できるため、
初心者の方でも何をしているコンポーネントなのかが分かりやすくなります。
import React from "react";
import useCounter from "./useCounter"; // 作ったカスタムフックを読み込み
function App() {
const { count, increment, decrement, reset } = useCounter(0);
return (
<div>
<h1>カウント: {count}</h1>
<button onClick={increment}>+1</button>
<button onClick={decrement}>−1</button>
<button onClick={reset}>リセット</button>
</div>
);
}
export default App;
ここで注目したいのは、コンポーネント側ではカウントの計算方法を一切意識していない点です。
「増やす」「減らす」「戻す」といった処理の中身はすべてカスタムフックに隠れています。
そのため、同じカウンターを別の画面で使いたくなった場合でも、
useCounterを呼び出すだけで同じ動きをすぐに再現できます。
カスタムフックを使うことで、コードが整理され、修正や追加もしやすくなるのが大きな魅力です。
4. カスタムフックを使うメリット
カスタムフックを使うと、次のようなメリットがあります。
- コードの重複を減らせる:同じロジックを複数のコンポーネントで使い回せます。
- 見通しの良いコードになる:UI(見た目)とロジック(処理)を分けることで、管理がしやすくなります。
- テストがしやすい:ロジックをひとつの関数にまとめることで、動作確認が簡単になります。
たとえば、「ウィンドウサイズの変更を監視する処理」や「APIデータを取得する処理」など、アプリ全体でよく使う機能はカスタムフックにまとめておくと便利です。
5. もう少し実用的な例:ウィンドウサイズを取得するカスタムフック
次に、実際のアプリでよく使う「画面サイズ(ウィンドウサイズ)」をリアルタイムで取得するカスタムフックを作ってみましょう。useEffectというフックを使い、ウィンドウのサイズが変わるたびに値を更新します。
import { useState, useEffect } from "react";
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight,
});
};
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return size;
}
export default useWindowSize;
import React from "react";
import useWindowSize from "./useWindowSize";
function App() {
const { width, height } = useWindowSize();
return (
<div>
<h1>画面の幅: {width}px</h1>
<h2>画面の高さ: {height}px</h2>
</div>
);
}
export default App;
このように、カスタムフックを使うと「動きを持つロジック」も簡単に再利用できます。特にReact開発では、チーム開発でもカスタムフックを共有して使うのが一般的です。
6. カスタムフックを作るときの注意点
カスタムフックはとても便利ですが、作るときにいくつか気を付けたいポイントがあります。 特に初心者のうちは「どこまでをカスタムフックにするか」を意識すると、使いやすい形になります。 すべてをまとめすぎると、逆に中身が分かりにくくなってしまうこともあります。
基本的には「複数のコンポーネントで同じ処理を使い回したいかどうか」を判断基準にするとよいでしょう。 画面ごとに内容が大きく違う処理は無理にカスタムフックにせず、 本当に共通化したいロジックだけを切り出すのがコツです。
また、カスタムフックの中ではフックの呼び出し順を変えないことも重要です。
条件分岐の中でuseStateやuseEffectを呼ばないようにし、
常に同じ順番で実行されるように書くことで、予期しない不具合を防げます。
7. カスタムフックとコンポーネントの役割分担
Reactでは、カスタムフックとコンポーネントの役割をはっきり分けると、 アプリ全体の見通しがよくなります。 コンポーネントは「何を表示するか」に集中し、 カスタムフックは「どんな動きをするか」を担当するイメージです。
たとえば、ボタンの配置や文字の表示はコンポーネント側に書き、 数値の計算や状態の管理はカスタムフックに任せます。 こうすることで、画面のデザインを変更したい場合でも、 ロジック部分にほとんど手を加えずに済みます。
初心者の方は、まず「表示用のコードが長くなってきたらロジックを外に出せないか」 と考えてみるとよいでしょう。 その一歩が、自然とカスタムフックを使いこなす力につながります。
8. カスタムフックを使い始めるおすすめの場面
「どんなときにカスタムフックを使えばいいのか分からない」と感じる方も多いかもしれません。
最初は難しく考えず、同じuseStateやuseEffectの組み合わせを
何度も書いている場面を探してみましょう。
たとえば、複数の画面でカウンターを使っていたり、 入力フォームの値管理を毎回書いていたりする場合は、 カスタムフックにまとめる良いタイミングです。 一度作ってしまえば、次からは呼び出すだけで同じ動きが再現できます。
小さな処理からカスタムフックに慣れていくことで、 Reactの考え方やコード整理の感覚も自然と身についていきます。 無理に難しい例を作らず、身近な処理から試していくのがおすすめです。
まとめ
React開発において、コンポーネントの肥大化を防ぎ、コードの保守性を高めるために「カスタムフック」の活用は欠かせません。ここまで学んできた通り、カスタムフックの本質は「ロジックの再利用」にあります。これまで各コンポーネントの中に散らばっていた useState や useEffect を一つの関数に集約することで、UI(見た目)とロジック(振る舞い)を明確に分離できるようになります。
初心者がカスタムフックを作成する際に意識すべきポイントは、何よりもまず「use」から始まる命名規則を守ることです。これにより、Reactの静的解析ツールが適切にフックのルールをチェックできるようになり、予期せぬバグを未然に防ぐことができます。また、状態管理だけでなく、外部APIとの通信やイベントリスナーの設定など、副作用を伴う処理こそカスタムフックの真価が発揮される場面です。
さらに、カスタムフックを導入することでテストの効率も格段に上がります。画面の表示を気にすることなく、純粋に「データが正しく処理されているか」というロジック部分だけを独立して検証できるからです。これは、将来的にプロジェクトの規模が大きくなった際に、チーム全体の開発スピードを支える大きな基盤となります。
最後に、より実践的な応用例として、入力フォームの値を管理するためのカスタムフック useInput を紹介します。複数の入力項目がある場合でも、このフック一つで記述を劇的に簡略化することが可能です。
import { useState } from "react";
// 入力フォームのロジックを共通化するカスタムフック
function useInput(initialValue) {
const [value, setValue] = useState(initialValue);
const handleChange = (e) => {
setValue(e.target.value);
};
const reset = () => setValue(initialValue);
return {
value,
onChange: handleChange,
reset,
};
}
export default useInput;
import React from "react";
import useInput from "./useInput";
function ContactForm() {
// 名前とメールアドレスの入力をカスタムフックで管理
const nameInput = useInput("");
const emailInput = useInput("");
const handleSubmit = (e) => {
e.preventDefault();
alert(`送信内容: ${nameInput.value} (${emailInput.value})`);
nameInput.reset();
emailInput.reset();
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>名前: </label>
<input type="text" {...nameInput} />
</div>
<div>
<label>メール: </label>
<input type="email" {...emailInput} />
</div>
<button type="submit">送信</button>
</form>
);
}
export default ContactForm;
このように、スプレッド構文 {...nameInput} を使うことで、 value や onChange を一行で渡せるようになり、コンポーネント側が非常にすっきりします。カスタムフックをマスターすることは、Reactエンジニアとして「読みやすく、壊れにくいコード」を書くための第一歩です。まずは身近な小さな処理から切り出す練習を始めてみましょう。
生徒
「先生、カスタムフックを実際に作ってみたら、App.jsの中身がすごく短くなって驚きました!今までは全部一つのファイルに詰め込んでいたので…。」
先生
「素晴らしい気づきですね!コンポーネントが『何を表示するか』に専念できるようになった証拠です。コードが読みやすくなったと感じませんか?」
生徒
「はい、どこに何の処理が書いてあるか一目で分かるようになりました。でも、どんな処理でもカスタムフックにした方がいいんでしょうか?」
先生
「良い質問です。基本的には『二箇所以上で同じような useState や useEffect を使っているな』と思ったら切り出し時です。逆に、その画面でしか使わない簡単な処理なら、無理に外に出すとファイルが増えて管理が面倒になることもありますよ。」
生徒
「なるほど、バランスが大事なんですね。あと、 useInput の例で {...nameInput} って書いてあったのが魔法みたいで面白かったです!」
先生
「ふふ、あれはJavaScriptの便利な書き方ですね。カスタムフックを使うと、Reactの機能を自分専用の使い勝手の良い道具に作り変えることができるんです。どんどん自分だけの『便利ツール』を増やしていってくださいね。」