React を使っているとよく登場するフックがuseState
・useEffect
・useMemo
です。
これについてたくさんの記事があると思いますが,アウトプットとして書きます
useState
役割
コンポーネント内で「状態(state)」を持たせるためのフック。
状態が更新されるとコンポーネントが再レンダリングされます。
使うケース
入力フォームの値
APIから取得したデータを保持する
サンプル
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>クリック数: {count}</p>
<button onClick={() => setCount(count + 1)}>増やす</button>
</div>
);
}
useEffect
役割
副作用(side effects)を扱うためのフック。
コンポーネントのレンダリング後に「外部とのやりとり」や「初期処理」を行いたいときに使います。
使うケース
初回マウント時に API からデータを取得
イベントリスナーやタイマーの登録/解除
DOM の直接操作
サンプル
function Users() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((data) => setUsers(data));
}, []); // 初回マウント時のみ実行
return (
<ul>
{users.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}
useMemo
役割
「計算コストが高い処理」をキャッシュするためのフック。
依存配列の値が変わったときだけ再計算され、それ以外は前回の結果を再利用します。
使うケース
配列やオブジェクトの重い計算結果をキャッシュしたい
子コンポーネントへの props で毎回新しい参照が作られるのを避けたい
サンプル
function IncomeTotal({ rows }: { rows: { amount: number }[] }) {
// rows が変わった時だけ合計を計算
const total = useMemo(() => {
console.log("計算したよ!");
return rows.reduce((sum, row) => sum + row.amount, 0);
}, [rows]);
return <div>収入合計: {total}</div>;
}
もしuseMemo
を使わなければ、レンダリングのたびにreduce
が実行されます。
処理が軽ければ問題ありませんが、数千件のデータ処理やグラフ用データの整形など、重い計算を繰り返すとパフォーマンスが悪化します。
useEffectとuseMemoの違い
どちらも依存配列を受け取って,依存が変わったときに「動く」イメージがあるが,用途と目的が違う.
useEffect の特徴
レンダリングが完了したあとに実行される
主に外部とのやりとりを扱う
戻り値は使えない(=return した値は次回に引き継げない)
useMemo の特徴
レンダリングの最中に実行される
主に「計算コストが高い処理の結果」をキャッシュする
戻り値をそのまま変数として利用できる
使い分けの基準まとめ
フック | 主な用途 | 具体例 |
---|---|---|
useState | UIに反映させたい値の管理 | 入力値、モーダル開閉、API結果の保持 |
useEffect | 副作用の処理(外部とのやりとり) | データフェッチ、イベント登録、DOM操作 |
useMemo | 計算コストの高い処理のキャッシュ | 集計、フィルタリング、グラフ用データ生成 |
実際のケーススタディ
例えば「家計簿アプリの月次サマリー」を作るとします。
APIから収支データを取得する → useEffect
API結果を保持してUIに表示する → useState
データから合計・収支を計算する → useMemo
(データ量が多いなら)
useEffect(() => {
fetch("/transactions/summary")
.then((res) => res.json())
.then((data) => setRows(data));
}, []);
const incomeTotal = useMemo(() => calculateIncome(rows), [rows]);
const expenseTotal = useMemo(() => calculateExpense(rows), [rows]);
const balance = useMemo(() => incomeTotal - expenseTotal, [incomeTotal, expenseTotal]);
まとめ
useState
:UIに反映させたい「状態」を持たせる
useEffect
:データフェッチや外部処理など「副作用」を扱う
useMemo
:計算結果をキャッシュして「無駄な再計算を避ける」