見出し画像

【標準機能でここまでやる】React Context × useReducer で作る「軽量・最強」のグローバル状態管理


86日目に useReducer で状態更新のルールを整理しました。本日87日目は、その知識をさらに広げ、アプリ全体で状態を共有する「Context API × useReducer(グローバル状態管理)」 を学習します。

外部ライブラリ(ReduxやZustand)に頼らなくても、標準機能だけで「大規模な状態管理」を作るプロのレシピを伝授します!

はじめに:Prop Drilling(バケツリレー)からの解放

コンポーネントツリーの深い階層にあるボタンのために、親から子、孫へと延々と props を渡す「バケツリレー」に疲れていませんか?

Context API を使えば、ツリーの頂点から「状態(State)」と「更新関数(Dispatch)」を直接地下深くへワープさせることができます。


💡 1. 組み合わせの魔法

Context だけだと「Stateの更新ロジック」がバラバラになりがちですが、useReducer を組み合わせることで、「状態の保持(Context)」と「更新ルールの集約(Reducer)」 という最強のタッグが組めます。

💻 実装例:グローバルな TodoProvider の作成

// 1. Contextの作成
const TodoContext = createContext(null);

// 2. Providerでstateとdispatchを共有
export const TodoProvider = ({ children }) => {
  const [state, dispatch] = useReducer(todoReducer, initialState);

  return (
    <TodoContext.Provider value={{ state, dispatch }}>
      {children}
    </TodoContext.Provider>
  );
};

💡 2. 使うときは「フック」を自作する

毎回 useContext(TodoContext) と書くのは面倒ですよね。専用のカスタムフックを用意するのが通なやり方です。

// 💡 コンポーネント側で簡単に使えるフック
export const useTodo = () => {
  const context = useContext(TodoContext);
  if (!context) throw new Error('useTodoはProviderの中で使ってください');
  return context;
};

// 💡 孫コンポーネントで使うだけ!
const TaskList = () => {
  const { state, dispatch } = useTodo();
  return <button onClick={() => dispatch({ type: 'DELETE', id: 1 })}>削除</button>;
};

💡 3. いつ使うべきか?(見極めポイント)

このパターンは非常に強力ですが、すべてのアプリで使う必要はありません。

  • 向いている: 認証状態(ログインユーザー)、テーマ設定(ダークモード)、複雑なフォーム状態。

  • 避けるべき: 頻繁に更新されるステータス(タイマーの秒数など)。Contextは更新されると、そのContextを使っているすべてのコンポーネントを再レンダリングしてしまうからです。


📝 87日目のまとめと課題

✅ 本日の習得ポイント

  • Providerパターン: アプリのルートで状態を包み込む「ストア」の作り方。

  • カスタムフックの抽象化: コンポーネントを Context の存在から隔離する(useTodo のように呼び出せるようにする)。

  • パフォーマンスへの配慮: 頻繁に更新されるものには使わないという「引き算」の設計。

📝 87日目の課題

  1. これまでに作ったアプリで、複数の場所から「同じ状態(例:ログイン中のユーザー情報やToDoリスト)」を参照している箇所を探しましょう。

  2. その状態を Context に移動させ、propsの受け渡しを減らしてスッキリさせてみてください。


もっとReactを学びたい人は!

#駆け出しエンジニアと繋がりたい #プログラミング学習 #React #100DaysOfCode #今日の積み上げ

いいなと思ったら応援しよう!