Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Redux と状態管理

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Redux と状態管理

Avatar for Jeongmin LEE

Jeongmin LEE

February 25, 2026
Tweet

More Decks by Jeongmin LEE

Other Decks in Programming

Transcript

  1. 状態(state)は、UIや挙動を決めるデータ 検索条件モーダル非表示 条件の変更ボタンクリック 検索条件モーダル表示 showModal が false だから モーダルを表示しない showModal

    を true に更新 showModal が true だから モーダルを表示する React の思想: UI = f(state) 状態(state)が変わると UI が再計算される
  2. ローカル状態とグローバル状態 ローカル状態(Local State) あるコンポーネント(または近い範囲)だけで完結する状態 Component Component Component Component Component Component

    Component Component Component Component 状態が特定のコンポーネント (+近い子孫)でだけ使われる React の場合 useState で 管理することが多い 例)テキスト検索UIで文字入力したら下にサジェスト表示する場合など
  3. 1. ヘッダーのメッセージアイコンに「🔴1(未読あり)」がついている 2. クリックしてメッセージを読み、既読にする 3. 通知バッジの「🔴1」が消えない、もしくは一度消えたのに別の操作をするとまた復活する message.isRead メッセージが既読か未読か管理する thread.hasUnread このスレッドに未読があるか管理

    header.unreadCount 未読数はいくつあるか管理 原因:特定の値を複数個所で「別々の状態」としてもっていてた メッセージモデル スレッドモデル ヘッダーバッジモデル グローバル状態関連バグの一例(チャットアプリの場合) グローバル状態は管理がとても大変
  4. 1. ヘッダーのメッセージアイコンに「🔴1(未読あり)」がついている 2. クリックしてメッセージを読み、既読にする 3. 通知バッジの「🔴1」が消えない、もしくは一度消えたのに別の操作をするとまた復活する message.isRead メッセージが既読か未読か管理する thread.hasUnread このスレッドに未読があるか管理

    header.unreadCount 未読数はいくつあるか管理 原因:特定の値を複数個所で「別々の状態」としてもっていてた メッセージモデル スレッドモデル ヘッダーバッジモデル ユーザーがメッセージを既読にしました! 「このメッセージは既読」更新✅ 「スレッドに未読なし」更新✅ 更新漏れ ❌ ヘッダーに未読が残ったまま グローバル状態関連バグの一例(チャットアプリの場合) グローバル状態は管理がとても大変
  5. グローバル状態をいい感じに管理するためのライブラリ Redux (2015) Mobx (2015) Zustand (2019) Jotai (2020) Recoil

    (2020) ※ 各ライブラリーの思想はそれぞれ違う ※ クライアントの状態管理ライブラリだけに絞ってる(サーバー側の状態管理の話は省略する) XState (2018)
  6. Redux における状態管理の登場人物 Action 何が起きたかを表すただのオブジェクト 「メッセージ id が ‘m1’ のメッセージを既読にする」という内容の依頼書 ❗状態は直接変更しない❗

    状態を変更する唯一の方法は Action を発行することである state が変更される度に Action が残るので、追跡できる Redux 3原則その2. 状態は読み取り専用
  7. Redux における状態管理の登場人物 Action 何が起きたかを表すただのオブジェクト 「メッセージ id が ‘m1’ のメッセージを既読にする」という内容の依頼書 ❗状態は直接変更しない❗

    状態を変更する唯一の方法は Action を発行することである state が変更される度に Action が残るので、追跡できる Redux 3原則その2. 状態は読み取り専用 例えると、、 会社のお金が金庫に全部現金で保管しているとして、 各部署が勝手に予算をそれぞれに持っていってる感じ (誰がいついくらもっていたかわからなくなってカオス)
  8. Redux における状態管理の登場人物 Reducer Reducer が純粋関数の場合 従来の状態を INPUT すると新しい状態を返す 従来の状態 {

    count: 1 } Reducer count に1を 足した状態を返す 新しいの状態 { count: 2 } Reducer に同じ state を渡して 100回実行しても常に { count: 2 } を返す ※ 入力が変わらないので、次の状態は入力だけで決まる(=予測しやすい) ※ 入力が変わらないので、テストが独立する(=テストしやすい)
  9. Redux における状態管理の登場人物 Reducer Reducer が純粋関数の場合 従来の状態を INPUT すると新しい状態を返す 従来の状態 {

    count: 1 } Reducer count に1を 足した状態を返す 新しいの状態 { count: 2 } Reducer に同じ state を渡して 100回実行しても常に { count: 2 } を返す ※ 入力が変わらないので、次の状態は入力だけで決まる(=予測しやすい) ※ 入力が変わらないので、テストが独立する(=テストしやすい) 元のオブジェクトは変えずに、新しいオブジェクトを作って 変更を表現することをイミュータブル(immutable)と言う
  10. Redux における状態管理の登場人物 Reducer Reducer が純粋関数ではない場合 Reducer に同じ state を渡して 100回実行したら

    { count: 101 } になる ※ 入力が変わるので、次の状態が“過去”に依存する(=状態の次を予測するために過去の処理回数や順番など気にしないといけない) ※ 入力が変わるので、前のテストの結果が次のテストに影響する(=テストしづらい) 従来の状態を INPUT すると従来の状態が変わる 従来の状態 { count: 2 } Reducer 従来の count に1を足す
  11. Redux における状態管理の登場人物 Reducer Reducer が純粋関数ではない場合 Reducer に同じ state を渡して 100回実行したら

    { count: 101 } になる ※ 入力が変わるので、次の状態が“過去”に依存する(=状態の次を予測するために過去の処理回数や順番など気にしないといけない) ※ 入力が変わるので、前のテストの結果が次のテストに影響する(=テストしづらい) 従来の状態を INPUT すると従来の状態が変わる 従来の状態 { count: 2 } Reducer 従来の count に1を足す 元のオブジェクトを同じ参照のまま中身を変えることを ミュータブル(mutable)と言う 参照うんうんの詳しい話は↓のスライドを参考にしよう
  12. Redux における状態管理の登場人物 • Store: グローバル状態をすべて保管している場所(オブジェクト) • Action: 何が起きたかを表す依頼書(オブジェクト) • Dispatch:

    Action を Store に送る(関数) • Reducer: Action を受け取り変更が反映された新しい状態を返す(関数) Redux の3原則 • Store は1つであるべき: 状態があちこちに散らばらないため、更新漏れが防止できる • 状態は読み取り専用: 状態を変更する唯一の方法は Action を発行すること • Reducer は純粋関数であるべき: 同じ入力なら同じ出力なのでテストできる、予測できる
  13. 状態更新の流れ Store Component Component Component View Action Dispatch 2. Action

    を Dispatch する Event Handler 1. 状態更新イベントが発生する
  14. 状態更新の流れ Store Component Component Component View Action Dispatch 2. Action

    を Dispatch する Event Handler Reducer State 3. Reducer が Action を受け取って 新しい状態を生成・Store に反映する 1. 状態更新イベントが発生する
  15. 状態更新の流れ Store Component Component Component View Action Dispatch 2. Action

    を Dispatch する Event Handler Reducer State 3. Reducer が Action を受け取って 新しい状態を生成・Store に反映する 4. Store の状態を参照している すべてのUIに状態変更が反映される 1. 状態更新イベントが発生する
  16. 状態更新の流れ Store Component Component Component View Action Dispatch 2. Action

    を Dispatch する Event Handler Reducer State 3. Reducer が Action を受け取って 新しい状態を生成・Store に反映する 4. Store の状態を参照している すべてのUIに状態変更が反映される 1. 状態更新イベントが発生する 状態が変わる場所・理由・ルールが決まってる → いつ・どこで・何の状態が変わったか追跡できる
  17. Dispatch するとき自動生成された Action Creator 呼び出すだけ Action Type、Action Creator 自動生成してくれる name

    と Reducer 名を組み合わせて Action Type を自動生成してくれる (この例だと “messages/MARK_AS_READ_REQUEST”) Action Creator も自動生成してくれる Redux Toolkit で slice 登場
  18. 内部的な Immer 採用により ... 地獄から解放 Redux Toolkit で slice 登場

    ※ Immerとは 直接データを変える書き方(ミューテーション)をしても、 裏側で安全な新しいコピー(イミュータブルな状態)を作ってくれるライブラリ このように状態を直接書き換える書き方をし ても、裏側で勝手にミュータブル(INPUTを直 接変更せず新しい状態を返す)にしてくれる
  19. なにもかもすべて全部 Store に入れない Redux はグローバル状態管理ライブラリなので、グローバル状態にすべきものだけを Store に入れる ❌特定の箇所で使われる状態を Store に入れて管理しよう

    あるコンポーネント(または近い範囲)だけで完結する状態 Component Component Component Component Component Component Component Component Component Component 例)テキスト検索UIで文字入力したら下にサジェスト表示する場合など Store useState で事足り案件をわざわざ Action 発行してそれを Dispatch して Reducer 作成して Store に格納する意味が ないし、無駄にアプリケーションが複雑になる
  20. なにもかもすべて全部 Store に入れない Redux はグローバル状態管理ライブラリなので、グローバル状態にすべきものだけを Store に入れる Component Component Component

    Component Component Component Component Component Component Component Store Front-end Back-end Database request response ❌APIから取得したデータも全部 Store に入れる
  21. なにもかもすべて全部 Store に入れない Redux はグローバル状態管理ライブラリなので、グローバル状態にすべきものだけを Store に入れる Component Component Component

    Component Component Component Component Component Component Component Store Front-end Back-end Database request response ❌APIから取得したデータも全部 Store に入れる APIレスポンスのDBのコピー(DBが真実)
  22. なにもかもすべて全部 Store に入れない Redux はグローバル状態管理ライブラリなので、グローバル状態にすべきものだけを Store に入れる Component Component Component

    Component Component Component Component Component Component Component Store Front-end Back-end Database request response APIレスポンスのDBのコピー(DBが真実) ❌APIから取得したデータも全部 Store に入れる → 非同期で取得(成功・失敗・ローディング) → 時間が経つと古くなる(再取得が必要) → キャッシュが必要になる可能性有 → …
  23. なにもかもすべて全部 Store に入れない Redux はグローバル状態管理ライブラリなので、グローバル状態にすべきものだけを Store に入れる Component Component Component

    Component Component Component Component Component Component Component Store Front-end Back-end Database request response APIレスポンスのDBのコピー(DBが真実) ❌APIから取得したデータも全部 Store に入れる → 非同期で取得(成功・失敗・ローディング) → 時間が経つと古くなる(再取得が必要) → キャッシュが必要になる可能性有 → … Redux はクライアント状態管理のライブラリな ので、それでサーバー状態をやろうとするとやや こしくなるし、無駄に Store が膨らむ サーバー状態: 真実がDB/APIにあるデータ クライアント状態: 真実がFEにあるデータ
  24. なにもかもすべて全部 Store に入れない Redux はグローバル状態管理ライブラリなので、グローバル状態にすべきものだけを Store に入れる Component Component Component

    Component Component Component Component Component Component Component Store Front-end Back-end Database request response APIレスポンスのDBのコピー(DBが真実) ❌APIから取得したデータも全部 Store に入れる → 非同期で取得(成功・失敗・ローディング) → 時間が経つと古くなる(再取得が必要) → キャッシュが必要になる可能性有 → … サーバー状態(真実がDB/APIにあるデータ)は専用の ライブラリで管理したほうが良い