Reactでは状態(state)とその更新ロジックをセットにした
「カスタムフック」
を自分で作ることができます!
これは状態管理を再利用可能にしたいときにめっちゃ便利です。
これは、コンポーネント(コード)の見通しが良くなり、
デカルトの4つの方法的規則の総合の規則に則ることができ、
開発効率がグンと上がります。
🧠 デカルトの4つの方法的規則(参考)
- 明証の規則(明晰判明なもの以外は真としない)
- 分析の規則(困難は分割せよ)
- 総合の規則(単純なものから複雑なものへ)
- 枚挙の規則(完全な列挙と見直しを行う)
逆に言えば、
・複数のコンポーネントからの再利用。
・コードの見通しが良くなる。
以外の使い道は無いという事ですが・・・
✅ カスタムフックの基本形
たとえば、「カウントアップ機能」を
状態(state) + ロジック(処理)ごとにまとめた
・カスタムフックの名前は必ずuseを始めに付ける。(フックは必ずuseから始まる。)
・引数や戻り値を持ち、戻り値による分割代入が行われる事が多い。
コンポーネントからの呼出しの記述const { 状態(state), state処理関数名1,state処理関数名2、・・・・ } = カスタムフック名(初期値);
上記を記述し、別ファイルにカスタムフック名で受け取るstateと関数を記述すれば、
コードの見通しがかなり改善します。
あとは、return()内で各子コンポーネントを呼び出す際に、state処理関数名を指定してやれば、処理したstateをreturnしてくれます。
カスタムフックを作る例を見てみましょう:
jsx// useCounter.js//引数もとれるし、複数の変数ならオブジェクトでOK
import { useState } from 'react';
function useCounter(initialValue = 0) {
//状態(state):count足し算、引き算、状態のreset
const [count, setCount] = useState(initialValue);
//ロジック(処理):
const increment = () => setCount((prev) => prev + 1);
const decrement = () => setCount((prev) => prev - 1);
const reset = () => setCount(initialValue);
return { count, increment, decrement, reset };
}
export default useCounter;
🧪 使い方(他のコンポーネントから)
jsximport React from 'react';
import useCounter from './useCounter';
function CounterComponent() {
const { count, increment, decrement, reset } = useCounter(5); // 引数5で戻り値を分割代入する。基本的にカスタムフックは戻り値の分割代入を使う。
return (
<div>
<h2>カウント: {count}</h2>
<button onClick={increment}>+</button>
<button onClick={decrement}>−</button>
<button onClick={reset}>リセット</button>
</div>
);
}
📦 カスタムフックにできること
カスタムフックの中では、以下のようなReactの機能を全部使えます:
useStateuseEffectuseRefuseContext- 他のカスタムフックの呼び出し など
つまり、
複雑な状態とそのロジックを
“関数化”
して再利用可能にできる
ってことです!
🧠 メリット
- 状態ロジックをまとめて再利用できる
- UIから状態管理を分離できる(=スッキリしたコードに)
- チーム開発でもロジックの共通化がしやすい
「フォーム入力管理」とか「APIフェッチ」なんかも、state + ロジックの塊なので、
全部カスタムフックにできます。
例えば、
顧客情報の入力フォームで使えるカスタムフックの例
👨💼 想定する顧客情報の項目
例として以下の項目を管理したいとします:
- 氏名(
name) - メールアドレス(
email) - 電話番号(
phone)
このデータを管理し、入力変更ロジック・初期化・バリデーションをまとめた
カスタムフック
を作ります。
🧩 カスタムフック:useCustomerForm.js
jsx// useCustomerForm.js
import { useState } from 'react';
function useCustomerForm(initialValues = {
name: '',
email: '',
phone: ''
}) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
// 入力変更ハンドラ
const handleChange = (e) => {
const { name, value } = e.target;
setValues((prev) => ({
...prev,
[name]: value,
}));
};
// バリデーション(簡易版)
const validate = () => {
const newErrors = {};
if (!values.name) newErrors.name = '名前を入力してください';
if (!values.email.includes('@')) newErrors.email = '正しいメールを入力してください';
if (values.phone && !/^\d{10,11}$/.test(values.phone)) {
newErrors.phone = '電話番号は10〜11桁の数字で入力してください';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
// リセット
const resetForm = () => {
setValues(initialValues);
setErrors({});
};
return {
values,
errors,
handleChange,
validate,
resetForm,
};
}
export default useCustomerForm;
🧪 フォームでの使い方
jsx// CustomerForm.js
import React from 'react';
import useCustomerForm from './useCustomerForm';
function CustomerForm() {
const {
values,
errors,
handleChange,
validate,
resetForm
} = useCustomerForm(); //分割代入(数が合わなくても名前が合えば代入可能)で戻り値を代入
const handleSubmit = (e) => {
e.preventDefault();
if (validate()) {
console.log('送信データ:', values);
alert('送信完了!');
resetForm();
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>名前:</label>
<input name="name" value={values.name} onChange={handleChange} />
{errors.name && <span style={{ color: 'red' }}>{errors.name}</span>}
</div>
<div>
<label>メール:</label>
<input name="email" value={values.email} onChange={handleChange} />
{errors.email && <span style={{ color: 'red' }}>{errors.email}</span>}
</div>
<div>
<label>電話番号:</label>
<input name="phone" value={values.phone} onChange={handleChange} />
{errors.phone && <span style={{ color: 'red' }}>{errors.phone}</span>}
</div>
<button type="submit">送信</button>
<button type="button" onClick={resetForm}>リセット</button>
</form>
);
}
export default CustomerForm;
🔁 まとめ:このカスタムフックで何が嬉しいの?
エラー表示やリセットも一括管理可能
フォームの状態(入力値)とロジック(変更・バリデーション)を分離できる
フォームの数が増えても便利なフックを作っておけば、同じフックを使い回せます。

