😀

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 パフォーマンスが良い

まとめ

  1. 最初はuseStateで十分
  2. props地獄を感じたらuseContext
  3. パフォーマンスや構造が複雑になったらZustand

Discussion