導入
「カウンターの増減」「ON/OFFの切り替え」のように、state を伴う似た処理が複数のコンポーネントで必要になることがあります。コピペで増やしていくと、直すときに全部を直す羽目になります。React では use で始まる自作の関数(カスタムフック)にロジックを切り出すことで、この重複をなくせます。
説明
カスタムフックは「中で useState などのフックを呼ぶ、ふつうの関数」です。名前を use で始めるのが決まりごとで、これによって React(と ESLint)が「これはフックだ」と認識できます。
// カスタムフック:カウンターのロジックだけを持つ
function useCounter(initial = 0) {
const [count, setCount] = React.useState(initial);
const increment = () => setCount((c) => c + 1);
const decrement = () => setCount((c) => c - 1);
const reset = () => setCount(initial);
return { count, increment, decrement, reset };
}
function Counter({ label }) {
const { count, increment, decrement, reset } = useCounter(0);
return (
<div style={{ border: "1px solid #94a3b8", borderRadius: 8, padding: 12, marginBottom: 8 }}>
<p>{label}: {count}</p>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
<button onClick={reset}>リセット</button>
</div>
);
}
function App() {
return (
<div style={{ fontFamily: "system-ui", maxWidth: 320 }}>
<Counter label="りんごの数" />
<Counter label="みかんの数" />
</div>
);
}
useCounterの中身は、実は今まで書いてきたuseState+ ハンドラそのものです。それを関数として切り出しただけです。<Counter label="りんごの数" />と<Counter label="みかんの数" />は、それぞれが独立してuseCounterを呼んでいます。だから片方を+1しても、もう片方の数字は変わりません(ロジックは共有していますが、state そのものはコンポーネントごとに別々です)。- カスタムフックは値だけでなく、
useEffectを含めた任意のフックの組み合わせを隠せます。「よく使う定型パターン」をまとめる場所だと考えてください。
やってみよう
useToggle(initial = false) というカスタムフック(value と、反転させる toggle を返す)を作り、ON/OFFスイッチのボタンを2つ、App に独立して置いてみましょう。