😀
React初心者だった私が理解した:なぜ状態管理ライブラリが必要なのか
はじめに
この記事は、私が実際に学びながら理解した「状態管理ライブラリが必要になる理由」をまとめたものです。
同じようにReactを学んでいる方の参考になれば嬉しいです。
Step 1: useStateで始めた頃
最初に作ったのは、シンプルなダッシュボードアプリです。
期間(日次・週次・月次)を選んでデータを表示するだけの構成でした。
function App() {
const [period, setPeriod] = useState('daily');
return (
<>
<PeriodSelector period={period} setPeriod={setPeriod} />
<Dashboard period={period} />
</>
);
}
useStateでstateを持って、子に渡すだけのシンプルな作りです。
Step 2: 機能を増やしたら大変になった
そこに「履歴を表示する」コンポーネントを追加してみました。
App → Dashboard → TransactionHistory → HistoryItem → DetailView
periodを使うのは一番下のDetailViewだけなのに、
上から全部のコンポーネントに渡さないといけません。
これが「Props Drilling(バケツリレー)」と呼ばれているそうです
Step 3: useContextを使ってみて気づいたこと
そこでuseContextを使ってみました
これでpropsを渡さずに済みます。
const PeriodContext = createContext<any>(null);
function App() {
const [period, setPeriod] = useState('daily');
return (
<PeriodContext.Provider value={{ period, setPeriod }}>
<Dashboard />
<TransactionHistory />
</PeriodContext.Provider>
);
}
function DetailView() {
const { period } = useContext(PeriodContext);
return <div>期間: {period}</div>;
}
どのコンポーネントからでもperiodを直接使えるようになりました。
しかし、動かしてみると、useContextでは、Contextの値が変わると、そのContextを使用している(useContextしている)すべてのコンポーネントが再レンダリングされるという仕様でした。
const AppContext = createContext<any>(null);
function App() {
const [period, setPeriod] = useState('daily');
const [userId, setUserId] = useState('user1');
return (
<AppContext.Provider value={{ period, setPeriod, userId, setUserId }}>
<Dashboard />
<UserProfile />
</AppContext.Provider>
);
}
userIdを更新しても、Dashboardまで再レンダリングされてしまいます。
Step 4: Zustandを使って解決
次に試したのがZustandです。
使っているstateだけ再レンダリングされます。
const useStore = create((set) => ({
period: 'daily',
userId: 'user1',
setPeriod: (v: string) => set({ period: v }),
setUserId: (v: string) => set({ userId: v }),
}));
function Dashboard() {
const period = useStore((s) => s.period);
return <div>期間: {period}</div>;
}
function UserProfile() {
const userId = useStore((s) => s.userId);
return <div>ユーザー: {userId}</div>;
}
userIdを更新しても、Dashboardは再レンダリングされません。
コードも読みやすく、使い方も直感的でした。
Step 5: 今の自分なりの結論
| 状況 | ベストな方法 | 理由 |
|---|---|---|
| コンポーネント内で完結するstate | useState | 一番シンプルで十分 |
| アプリ全体で共有したいが更新が少ないstate | useContext | props渡し不要で便利 |
| 頻繁に更新・参照されるstate | Zustand | パフォーマンスが良い |
まとめ
- 最初はuseStateで十分
- props地獄を感じたらuseContext
- パフォーマンスや構造が複雑になったらZustand
Discussion