ReactのHooks(フックス)は、関数コンポーネントで状態管理やライフサイクルイベントを利用できる強力な機能です。React 16.8で導入され、クラスコンポーネントでしか扱えなかった機能を、関数コンポーネントでも簡単に使えるようになりました。これにより、コンポーネントの設計がシンプルかつ柔軟になり、コードの可読性が大幅に向上しました。本記事では、React Hooksの応用技術について解説し、複雑な状態管理や副作用の処理、さらにカスタムフックの作成方法を紹介します。
1. useStateフックで状態管理
useStateは、コンポーネントの内部で状態を管理するための基本的なフックです。このフックを使って、状態の初期値とその状態を更新するための関数をセットで取得します。
例:シンプルなカウントアップ機能
import React, { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>カウント: {count}</p>
<button onClick={() => setCount(count + 1)}>カウントアップ</button>
</div>
);
}
export default Counter; -
useState(0)でcountの初期値を0に設定し、setCountでその値を更新します。 - ボタンをクリックすると、
setCountが呼ばれ、countが1ずつ増加します。
2. useEffectフックで副作用の処理
- *
useEffect*は、コンポーネントのレンダリング時に副作用を処理するためのフックです。サーバーからデータをフェッチしたり、DOMを操作したりする場面で使われます。また、クリーンアップ処理も同時に行えます。
例:データフェッチとクリーンアップ
import React, { useState, useEffect } from "react";
function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
// データのフェッチ処理
fetch("https://api.example.com/data")
.then((response) => response.json())
.then((data) => setData(data));
// クリーンアップ処理(コンポーネントのアンマウント時に実行)
return () => {
console.log("コンポーネントがアンマウントされました");
};
}, []); // 第2引数が空の配列の場合、マウント時にのみ実行される
return <div>{data ? JSON.stringify(data) : "データを読み込み中..."}</div>;
}
export default DataFetcher; - 第2引数に
[]を指定すると、useEffectはコンポーネントがマウントされたときに1度だけ実行され、クリーンアップ処理はアンマウント時に行われます。 - サーバーからデータをフェッチし、状態にデータを格納することで、再レンダリング時にそのデータが表示されます。
3. useContextフックでコンテキストの共有
- *
useContext*は、コンポーネントツリーの深い階層にあるコンポーネントに、状態や関数を簡単に渡すためのフックです。これにより、いわゆる「プロップスドリリング」を避けることができます。
例:テーマの共有
import React, { useContext, useState } from "react";
const ThemeContext = React.createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
function ThemeButton() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
現在のテーマ: {theme}
</button>
);
}
function App() {
return (
<ThemeProvider>
<ThemeButton />
</ThemeProvider>
);
}
export default App; -
ThemeContextを作成し、useContextフックでテーマを変更できるようにしています。 -
ThemeProviderは、テーマの状態を持ち、そのテーマを子コンポーネントに供給します。
4. useReducerフックで複雑な状態管理
- *
useReducer*は、useStateよりも複雑な状態管理を行いたい場合に使われるフックです。複数の状態や、複数のアクションに基づく状態の変更が必要な場合に有効です。
例:カウントの増減機能
import React, { useReducer } from "react";
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>カウント: {state.count}</p>
<button onClick={() => dispatch({ type: "increment" })}>増加</button>
<button onClick={() => dispatch({ type: "decrement" })}>減少</button>
</div>
);
}
export default Counter; -
reducer関数が状態を管理し、dispatchでアクションをトリガーします。 -
useReducerを使うことで、複雑なロジックをシンプルに処理できます。
5. カスタムフックの作成
カスタムフックは、共通のロジックを再利用可能にするために使います。useStateやuseEffectなどのフックを組み合わせて、カスタムフックを作成することで、複数のコンポーネントで同じロジックを使い回せます。
例:ウィンドウサイズの監視
import { useState, useEffect } from "react";
function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return windowSize;
}
function WindowSizeDisplay() {
const size = useWindowSize();
return (
<div>
<p>ウィンドウの幅: {size.width}</p>
<p>ウィンドウの高さ: {size.height}</p>
</div>
);
}
export default WindowSizeDisplay; -
useWindowSizeはカスタムフックで、ウィンドウのサイズを監視し、そのサイズを返します。 - 複数のコンポーネントでこのカスタムフックを使用して、共通のロジックを再利用できます。
6. useMemoとuseCallbackでパフォーマンス最適化
- *
useMemoとuseCallback*は、コンポーネントの再レンダリング時に不要な処理を避け、パフォーマンスを最適化するために使います。
useMemoの例
import React, { useState, useMemo } from "react";
function ExpensiveCalculation(num) {
console.log("計算中...");
return num * 2;
}
function Calculator() {
const [count, setCount] = useState(0);
const [input, setInput] = useState("");
const result = useMemo(() => ExpensiveCalculation(count), [count]);
return (
<div>
<input value={input} onChange={(e) => setInput(e.target.value)} />
<p>計算結果: {result}</p>
<button onClick={() => setCount(count + 1)}>カウントアップ</button>
</div>
);
}
export default Calculator; -
useMemoを使って、countが変更されたときにのみ重い計算処理を実行します。
useCallbackの例
import React, { useState, useCallback } from "react";
function Child({ onClick }) {
return <button onClick={onClick}>子コンポーネントのボタン</button>;
}
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<p>カウント: {count}</p>
<Child onClick={handleClick} />
</div>
);
}
export default Parent; -
useCallbackを使って、handleClick関数を再レンダリングのたびに新しく生成しないようにしています。
まとめ
ReactのHooksは、関数コンポーネントで強力な状態管理や副作用の処理を可能にし、再利用可能なロジックをシンプルに構築できる画期的な機能です。useStateやuseEffectといった基本的なフックから、useReducerやカスタムフック、さらにはuseMemoやuseCallbackを使ったパフォーマンス最適化まで、Hooksを活用することで、複雑なアプリケーションでも効率的に開発が進められます。これらをマスターすることで、Reactの可能性を最大限に引き出しましょう。

