Reactの状態管理ってなに
Reactを学習上で避けて通れないのが状態管理です。
状態管理とは「アプリケーション内でユーザーの操作などによって変化するデータ(状態)を適切に管理し、UIに反映させる仕組み」です。
この記事では、useStateなどのフックの使い方を詳しくは解説せず、Reactの状態管理ってイマイチよくわからない。って人向けにReactの状態管理はこういう感じのことをするのかとイメージできることを目的としています。
そもそも状態とは何か
状態とはアプリケーションの「今の状況」を表すデータです。
現実の世界でいうと
- 電気のスイッチ:ON / OFF
- スマホの画面:明るい / 暗い
- 冷蔵庫の扉:開いている / 閉まっている
アプリケーションだと、
- ユーザーの入力値;空/入力中/入力済み
- ログイン状態:ログイン済み/未ログイン
- 表示・非表示の切り替え:表示・非表示の選択
など、すべて「状態」です。時間とともに変化し、その変化によって見た目や動作が変わります。
Reactでは状態のことを「State(ステート)」と呼びます。
状態がある・ない
// 静的なHTML(状態なし)
<button>クリックしてください</button>
<p>0</p>
// React(状態あり)
function Counter() {
const [count, setCount] = useState(0);
// 状態に応じて表示が変わる
return (
<div>
<button onClick={() => setCount(count + 1)}>
クリックしてください
</button>
<p>{count}</p>
</div>
);
}
状態がない(静的)
- 常に同じ表示
- ユーザーの操作に反応しない
LPなどのHTML・CSSだけで作ったようなWebページは状態がないと言えます。
状態あり(動的)
- 時間とともに変化
- ユーザーの操作に反応する
Webアプリケーションのようにユーザーの操作によって表示が変わるものは状態があると言えます。
Reactにおける状態
Reactでは状態が変化するとUIが自動的に変化します。
従来のJavaScript(DOM操作、命令的)
let count = 0;
const button = document.getElementById('button');
const display = document.getElementById('display');
button.addEventListener('click', () => {
count++; // 状態を変更
display.textContent = count; // 手動でDOMを更新
});
従来の方法の問題点
- 状態の変更とUI更新を別々に管理する必要がある
- DOMを手動で操作する必要がある
- 状態とUIの同期が取れなくなりがち
- コードが複雑になりやすい
React(宣言的UI)
// 状態が変化すると、UIが自動的に更新される
const [count, setCount] = useState(0);
return (
<div>
<p>カウント: {count}</p> {/* 状態に応じて表示が変わる */}
<button onClick={() => setCount(count + 1)}>
増加
</button>
</div>
);
Reactの利点
- 状態を変更するだけで、UIが自動的に更新される
- 「どう変化するか」ではなく、「どうあるべきか」を記述する
- 状態とUIが常に同期している
- コードがシンプルで理解しやすい
宣言的と命令的
Reactはよく宣言的UIと言われますが、従来のDOM操作は命令的です。
従来のDOM操作だと、要素を取得、その要素に対して操作をします。
先ほどのコードだと
// 変数countを0で初期化
let count = 0;
// IDがbutton、displayの要素を取得
const button = document.getElementById('button');
const display = document.getElementById('display');
// buttonをクリックしたら、countやdisplayを変更するように指示している
button.addEventListener('click', () => {
count++; // 状態を変更
display.textContent = count; // displayにcountを挿入
});
と手順を細かく指示する必要があります。
そのため、手順が複雑になりやすくバグが起きやすいコードになります。
// React: 状態が変化すると、UIが自動的に更新される
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div>
<p>カウント: {count}</p> {/* 状態に応じて表示が変わる */}
<button onClick={() => setCount(count + 1)}>
増加
</button>
</div>
);
};
と最終的な結果を伝えるだけです。
そのため、シンプルでわかりやすくDOM操作はReactが行うためバグの少ないコードになります。
料理で例えると
1. フライパンを火にかける
2. 油を入れる
3. 卵を割る
4. フライパンに入れる
5. 3分待つ
6. ひっくり返す
7. 2分待つ
8. 火を止める
→どうやって目玉焼きを作るかの手順を1つずつ細かく指示
「美味しい目玉焼きが欲しい」
→ 目玉焼きという料理(?)の最終的な結果を伝える
🌐 仮想DOMによる効率的な更新
Reactは仮想DOMを使用することで効率的にUIの更新や状態管理をしています。
状態が変化すると、新しい仮想DOMが作成され、差分を計算。該当部分のみ更新がされます。
状態管理で意識すること
状態管理をしようとすると、なんでも管理しようとなりがちです。
適切な管理を意識することで、保守しやすくバグの少ないReactアプリケーションを作ることができます。
- シンプルに保つ - 必要最小限の状態だけ持つ
- 適切な場所に置く - 使う場所を考えて状態の位置を決める
- 不変性を守る - 既存の状態を直接変更しない
- 責任を分離する - 関連性の低い状態は分けて管理
- 非同期性を理解する - 状態更新のタイミングを把握
- 型安全性を保つ - 適切な初期値と型を使用
- パフォーマンスを意識する - 不要な再レンダリングを避ける
Reactの状態管理のフック
では、Reactで実際にどのようにして状態を管理するのでしょうか?
Reactでは状態管理のフックというものが用意されています。
ひとつひとつ細かく説明することでわかりづらくなると思いますので、ここでは簡単に解説します。
useState
Reactで最も基本的な状態管理の方法がuseStateです。コンポーネント内で状態管理をするためのフックです。
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0); // 初期値は0
return (
<div>
<p>現在のカウント: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(count - 1)}>-1</button>
</div>
);
}
useReducer
複雑な状態管理にはuseReducerを使います。状態の更新ロジックをコンポーネントから分離することができます。
import { useReducer } from "react";
// 状態の更新方法を定義
function counterReducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
case "reset":
return { count: 0 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
<p>カウント: {state.count}</p>
<button onClick={() => dispatch({ type: "increment" })}>+1</button>
<button onClick={() => dispatch({ type: "decrement" })}>-1</button>
<button onClick={() => dispatch({ type: "reset" })}>リセット</button>
</div>
);
}
useContext
一つずつのコンポーネント内だけで状態を管理すると不便なときがあります。アプリケーション全体で状態を共有したい場合はuseContextを使います。
import { createContext, useContext, useState } from "react";
// コンテキストを作成
const UserContext = createContext();
// プロバイダーコンポーネント
function UserProvider({ children }) {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}
// ユーザー情報を表示するコンポーネント
function UserProfile() {
const { user } = useContext(UserContext);
if (!user) {
return <p>ログインしていません</p>;
}
return <p>こんにちは、{user.name}さん!</p>;
}
// ログインボタンコンポーネント
function LoginButton() {
const { setUser } = useContext(UserContext);
const handleLogin = () => {
setUser({ name: "田中太郎", id: 1 });
};
return <button onClick={handleLogin}>ログイン</button>;
}
// アプリケーション全体
function App() {
return (
<UserProvider>
<UserProfile />
<LoginButton />
</UserProvider>
);
}
外部ライブラリ
Reactの標準機能だけでは状態管理をするのは難しくなります。Reactではさまざまな状態管理のライブラリがあります。
それぞれ特徴があるのでユースケースや好みに応じて使い分けましょう。
まとめ
状態管理とは「アプリケーションの今の状態をデータで表現し、変化に応じて更新する仕組み」です。
Reactでは状態の変化を検知してUIを自動的に更新します。
適切な状態管理により、ユーザー体験の向上、開発効率の改善、長期的な保守性の確保が可能になります。
Reactアプリケーションでは状態管理を理解していないと、期待したように値が更新されないことがあります。useReducerなどを詳しく解説した記事も執筆していきたいと思います。
ありがとうございました。