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は
何が起こった
かを記述するが、アプリケーションの状態の変化
は記述しない
- Actionsは
- Reducerは、以前の状態とActionを受け取り、新しい状態を返す
- Reducerは
純粋関数
でなければならない- Reducerでやってはいけないこと
- 受け取ったオブジェクト(state)に変更を加えること
- stateのコピーを作り、そのコピーに代入する
- e.g. stateとしてオブジェクトの配列が渡ってきてそれにappendしたい時、展開したstateの末尾に新たなオブジェクトを加えて返す
- return [...state, { text: action.text, completed: false }]
- e.g. stateとしてオブジェクトの配列が渡ってきてそれにappendしたい時、展開したstateの末尾に新たなオブジェクトを加えて返す
- stateのコピーを作り、そのコピーに代入する
- 副作用を引き起こすこと
- e.g. APIコール、ルーティングの変更
- 非純粋関数を呼び出すこと
- e.g. Date.now() or Math.random()
- 受け取ったオブジェクト(state)に変更を加えること
- Reducerでやってはいけないこと
- Reducerは
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
Action
とReducer
を組み合わせる役割Action
アプリケーションで何が発生したかを表すReducer
Actionに対して状態をどのように変化したかを表す
- Storeでできること
- アプリケーションの状態を保持する
getState()
を通じて、状態を取得できるようにするdispatch(action)
を通じて、状態の更新をできるようにするsubscribe(listener)
を通じて、リスナーを登録するsubscribe(listener)
の戻り値の関数を実行することで、リスナーの登録解除を行う
Data Flow
- データのライフサイクルは4つのステップに分かれている
store.dispatch(action)
の呼び出しStore
がReducer
を呼び出すroot Reducer
が複数のreducerの出力をアプリケーション全体の状態を表すオブジェクト(a single state tree
)にまとめる- root Reducerから返却された
a single state tree
をStoreが保存する
Usage with React
- ReactがReduxと接続すると、Reactは
Presentational Components
とContainer 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の設計手順が参考になりそう
- Break the UI Into A Component Hierarchy
- Build A Static Version in React
- Identify The Minimal(but complete) Representation Of UI State
- UI の state として持つべき最小限を特定する
DRY
がキーワード- 下記はstateとして持ってはならない
- 親要素からpropsとして渡されるもの
- 操作がお粉されても変わらないもの
- 他のstateやpropsから導き出せるもの
- 下記はstateとして持ってはならない
- Identify Where Your State Should Live
- Redux は single source of truth なので関係なさそう
- Add Inverse Data Flow
- Redux はデータの流れは一方向なので関係なさそう
- React Componentの設計手順が参考になりそう
- Container Componentsの設計
- Presentatinal ComponentsとReduxをつなげるContainer Componentsを設計する
- Presentational Components の設計
Componentsを作っていく手順
- Presentational Components を作る
- local state や lifecycle methods を使わずに、ステートレスな関数としてのコンポーネントを作る
- 関数でなくてもよいが、関数が簡単
- local stateやlifecycle methods、パフォーマンスの最適化を行う時に、関数をクラスに変更する
- local state や lifecycle methods を使わずに、ステートレスな関数としてのコンポーネントを作る
- 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 を返す
- connect()を使うために
- 内部的には類似の処理を行うが、パフォーマンスが最適化されている
- Conponent内でcontainerをまとめる
- 各ComponentにStoreを渡す
- 全てのComponentはRedux Storeにアクセスする必要があるがpropsとして渡していくのは煩雑
Provider
を使うと全てのContainerがStoreにアクセスできるようになる