Redux してみる

Redux公式ドキュメントを読んだりしたまとめ

Redux

  • Reduxは、アプリケーションの状態に関することを行う
  • Reactと同じような文脈だと思っていたが、ReactとReduxは別物
    • Reduxは状態やその変化を扱い、ReactはReduxによって管理された状態に応じてUIの描画を行う
    • AngularでもjQueryでもReduxを使うことができる
  • Reduxの3つの原則
    • Single Source of Truth
      • アプリケーション全体の状態は1つのオブジェクトとして保持される
    • State is read-only
      • 状態を変更する方法は何が発生したかを表すActionを発行すること
    • Changes are Made With Pure Functions
      • Actionによって状態がどのように変化するかを表すためにReducerという純粋関数を作る
  • 全体的な大まかな流れや図は下記記事が良さそう

Actions

  • アプリケーションからStoreに送られるjson
    • 何のアクションが実行されるかを示すtypeフィールドが必須
    • storeに対して、このaction以外を送ることはない
  • store.dispatch(action)を使って送られる
  • Action Creators
    • Actionを生成する関数

Reducers

  • Storeに送られるActionによってアプリケーションの状態がどのように変化するかを明記している
    • Actionsは何が起こったかを記述するが、アプリケーションの状態の変化は記述しない
  • Reducerは、以前の状態とActionを受け取り、新しい状態を返す
    • Reducerは純粋関数でなければならない
      • Reducerでやってはいけないこと
        • 受け取ったオブジェクト(state)に変更を加えること
          • stateのコピーを作り、そのコピーに代入する
            • e.g. stateとしてオブジェクトの配列が渡ってきてそれにappendしたい時、展開したstateの末尾に新たなオブジェクトを加えて返す
              • return [...state, { text: action.text, completed: false }]
        • 副作用を引き起こすこと
          • e.g. APIコール、ルーティングの変更
        • 非純粋関数を呼び出すこと
          • e.g. Date.now() or Math.random()
 Given the same arguments, it should calculate the next state and return it.
 No surprises.
 No side effects.
 No API calls.
 No mutations.
 Just a calculation.

Store

  • ActionReducerを組み合わせる役割
    • Action アプリケーションで何が発生したかを表す
    • Reducer Actionに対して状態をどのように変化したかを表す
  • Storeでできること
    • アプリケーションの状態を保持する
    • getState()を通じて、状態を取得できるようにする
    • dispatch(action)を通じて、状態の更新をできるようにする
    • subscribe(listener)を通じて、リスナーを登録する
    • subscribe(listener)の戻り値の関数を実行することで、リスナーの登録解除を行う

Data Flow

  • データのライフサイクルは4つのステップに分かれている
    1. store.dispatch(action)の呼び出し
    2. StoreReducerを呼び出す
    3. root Reducerが複数のreducerの出力をアプリケーション全体の状態を表すオブジェクト(a single state tree)にまとめる
    4. root Reducerから返却されたa single state treeをStoreが保存する

Usage with React

  • ReactがReduxと接続すると、ReactはPresentational ComponentsContainer Componentsに分かれる
Presentational Components Container Components
目的 見た目(markup, styles) 動き(fetch data, state updates)
Aware of Redux No Yes
To Read Data props からデータを受け取る Redux State からデータを受け取る
To Change Data props から受け取った関数を実行する Redux Action を Dispatch する
Are Written プログラマが記述する 通常は React Redux によって生成される
  • それぞれのComponentsの設計
    • Presentational Components の設計
      • React Componentの設計手順が参考になりそう
        1. Break the UI Into A Component Hierarchy
        2. Build A Static Version in React
          • データモデルを受け取って描画するだけのアプリケーションを作る
            • インタラクティブな要素とは分けることがポイント
              • 静的なバージョンは考えることは少ないがコーディングは多い
              • インタラクティブな要素の追加は考えることは多いがコーディングは少ない
        3. Identify The Minimal(but complete) Representation Of UI State
          • UI の state として持つべき最小限を特定する
          • DRYがキーワード
            • 下記はstateとして持ってはならない
              • 親要素からpropsとして渡されるもの
              • 操作がお粉されても変わらないもの
              • 他のstateやpropsから導き出せるもの
        4. Identify Where Your State Should Live
          • Redux は single source of truth なので関係なさそう
        5. Add Inverse Data Flow
          • Redux はデータの流れは一方向なので関係なさそう
    • Container Componentsの設計
      • Presentatinal ComponentsとReduxをつなげるContainer Componentsを設計する

Componentsを作っていく手順

  1. Presentational Components を作る
    • local state や lifecycle methods を使わずに、ステートレスな関数としてのコンポーネントを作る
      • 関数でなくてもよいが、関数が簡単
      • local stateやlifecycle methods、パフォーマンスの最適化を行う時に、関数をクラスに変更する
  2. Container Components を作る
    • Presentational Components と Redux をつなげる
    • Comtainer Component は store.subscribe() を実行して、Redux の state の一部を読み出し、Presentational Components にプロパティとして渡す
      • 内部的には類似の処理を行うが、パフォーマンスが最適化されているconnect()を実行することが推奨される
        • connect()を使うためにmapStateToProps()を定義する必要がある
          • 現在のRedux stateがpresentational Componentにどのようなpropsとして渡されるかを記述したもの(関数名のまま)
        • 同様にmapDispatchToProps()も定義する
          • dispatch()を受け取って、presentational Componentに渡す callback props を返す
  3. Conponent内でcontainerをまとめる
  4. 各ComponentにStoreを渡す
    • 全てのComponentはRedux Storeにアクセスする必要があるがpropsとして渡していくのは煩雑
    • Providerを使うと全てのContainerがStoreにアクセスできるようになる