データフロー
Reduxのアーキテクチャは、厳格な一方向のデータフロー を中心に設計されています。
アプリケーション内のすべてのデータが、同じライフサイクルパターンをたどるということです。これによりアプリのロジックがより予測しやすく、理解しやすくなります。また、データの正規化が促されます。そのためお互いにつながりが無い、同じデータの複製をいくつも作らなくてすみます。
まだピンとこないなら、モチベーションとFluxが役立つ場合(The Case for Flux)を読んでください。一方向のデータフローを支持する、説得力のある主張です。Reduxは正確にはFluxではないのですが、主な利点は同じです。
どんなReduxアプリでも、データのライフサイクルは次の4ステップをたどります:
store.dispatch(action)
を呼び出す。Actionは 起きたこと を表す単なるオブジェクトです。例えば:
{ type: 'LIKE_ARTICLE', articleId: 42 } { type: 'FETCH_USER_SUCCESS', response: { id: 3, name: 'Mary' } } { type: 'ADD_TODO', text: 'Reduxのドキュメントを読む。' }
Actionを、とても簡潔なニュースの断片だと考えてください。“メアリーは記事42にいいねした。” とか “‘Reduxのドキュメントを読む。’がTodoリストに追加された。” など。
アプリのどこからでも
store.dispatch(action)
を呼び出せます。コンポーネント(構成要素)やXHRコールバック、あらかじめ決められた間隔でも呼び出せます。Redux Storeが、与えられたReducer関数を呼び出す。
Storeは、2つの引数をReducerに渡します。現在の状態ツリーとActionです。例えばTodoアプリで、ルート(大元の)Reducerは次のような状態とActionを受け取るでしょう:
// 現在のアプリケーション状態(Todoのリストと、選択されたフィルター) let previousState = { visibleTodoFilter: 'SHOW_ALL', todos: [ { text: 'ドキュメントを読む。', complete: false } ] } // 実行されたAction(Todoの追加) let action = { type: 'ADD_TODO', text: '流れを理解する' } // Reducerは次のアプリケーション状態を返す let nextState = todoApp(previousState, action)
Reducerは純粋関数であることに注意してください。次の状態を 計算する だけです。完全に予測可能であるべきです。つまり同じ入力には、何度やっても同じ出力を返すのです。副作用は、どんなものでも行うべきではありません。例えば、API呼び出しやルート遷移など。これらは、ActionをDispatch(送信)する前に行うべきです。
ルートReducerは、複数のReducerの結果を1つの状態ツリーにまとめてよい。
ルートReducerをどのように構成するかは、すべてあなた次第です。Reduxは
combineReducers()
というヘルパー関数を提供しています。ルートReducerを複数の関数へ“分割する”のに役立ちます。それぞれの関数は、 状態ツリーの一部を処理します。combineReducers()
がどう機能するか示します。ここでは2つのReducerがあります。1つはTodoリストのため、もう1つは今選択されているフィルター設定のためです:function todos(state = [], action) { // 計算は省略... return nextState } function visibleTodoFilter(state = 'SHOW_ALL', action) { // 計算は省略... return nextState } let todoApp = combineReducers({ todos, visibleTodoFilter })
Actionを発行すると、
combineReducers
によって返されたtodoApp
は両方のReducerを呼び出します:let nextTodos = todos(state.todos, action) let nextVisibleTodoFilter = visibleTodoFilter(state.visibleTodoFilter, action)
そして両方の結果を、1つの状態ツリーにまとめます:
return { todos: nextTodos, visibleTodoFilter: nextVisibleTodoFilter }
combineReducers()
は手軽なヘルパー関数ですが、使わなくても構いません。ルートReducerの書き方は、あなた次第です!ReduxはルートReducerによって返された、完全な状態ツリーを保存します。
上記で、新しいツリーがアプリの次の状態となりました!
store.subscribe(listener)
で登録されたすべてのリスナーは、このとき呼び出されます。リスナーはstore.getState()
で現在の状態を得ることもできます。そして、UIの更新が可能となりました。現在の新しい状態を反映するためです。React Reduxのようなバインディング(連携プログラム)を使っているなら、このときに
component.setState(newState)
が呼び出されます。
次のステップ
これで、Reduxがどのように機能するか分かりました。次はReactアプリにつなげましょう。
上級ユーザーへの注意
基本的なコンセプトをよく理解していて、以前にこのチュートリアルを終えた方へ。上級チュートリアルの非同期なフローを忘れずに確認してください。Reducerに届く前に、ミドルウェアが非同期なActionをどう変換するか学べます。