ここではReduxでよく使われる用語をその型と併せて説明します。その型はFlowの表記を使って説明します。
type State = any
State (別名 state tree )はいろいろな意味を持ちます。通常、Redux APIでは、Storeによって管理され、getState()
で返される単独の状態を意味します。 それはReduxを使ったアプリケーション全体の状態を表します。多くの場合、その状態は複雑なObjectです。
State自体はObjectやMapのようなkey-value collectionであることが多いですが、技術的には任意の型で問題ありません。それでも、Stateをシリアライズできるようにするべきです。簡単にJSONに変換できない物はStateの中に入れないようにしてください。
type Action = Object
Action はStateを変更する注文を表す素のObjectです。ActionはデータをStoreに渡す唯一の方法です。つまり、UIイベントやネットワークコールバックやWebSocketのような他のソースからのデータは最終的にActionとしてdispatchする必要があります。
Actionにそれの種類を示すtype
フィールドを設定する必要があります。type
フィールドの値は定数として定義したり、他のモジュールからimportすることができます。type
フィールドの値はSymbolsより文字列の方が適しています。なぜなら、シリアライズできるからです。
type
フィールド以外のActionオブジェクトの構造は特に決まっていません。それに興味がある場合は、Flux Standard Actionが参考になると思います。
以下のAsync Actionも見ましょう。
type Reducer<S, A> = (state: S, action: A) => S
reducer (別名 reducing function) は累算結果と値を受け取って、新しい累算結果を返す関数です。 これはコレクションの値を1つの値に畳み込むために使われます。
ReducerはReduxだけの概念ではありません。関数型プログラミングの基本的な概念です。JavaScriptのようなほとんど関数型言語でない言語でも、reduceするためのAPIを備えています。JavaScriptのそれはArray.prototype.reduce()
です。
Reduxでは累算結果はStateオブジェクトです。そして、累算される値はActionです。Reducerは既存のStateとActionを受け取って新しいStateを算出します。Reducerは 純粋関数(Pure Function) である必要があります。つまり、同じ入力に対して完全に同じ出力を返す必要があります。
hot reloadingやtime travelのような素晴らしい機能を動作させるために、Reducerは副作用(side-effects)を含んでいてはいけません。
ReducerはReduxの最も重要なコンセプトです。
API呼び出しをReducerに入れないでください。
type BaseDispatch = (a: Action) => Action
type Dispatch = (a: Action | AsyncAction) => any
Dispatching Function (または、simply dispatch function )は、1つのActionもしくは1つのAsync Actionを受け取る関数です。この関数は1つのActionもStoreへdispatchしない場合と1つまたは複数のActionをStoreへdispatchする場合があります。
一般的なDispatching FunctionとStoreインスタンスのMiddlewareを通さないBase Dispatch
Functionは異なります。
Base Dispatch Functionは常に同期的なActionをStoreのReducerへ送ります。そのActionとReducerと既存のStoreのStateを使って、新しいStateを算出します。このActionは素のObjectにReducer用の値をセットした物です。
MiddlewareはBase Dispatch Functionをラップします。これによって、dispatch関数はActionに加えてAsync Actionを取り扱うことが可能になります。(Dispatching Functionになる。)MiddlewareはActionとAsync Actionを次のMiddlewareに渡す前に、それらを変換したり、遅延したり、無視したり、それ以外の処理を加える場合があります。詳しい説明は以下にあります。
type ActionCreator<A, P extends any[] = any[]> = (...args: P) => Action | AsyncAction
Action Creator は端的に言うとActionを生成する関数です。もう一度言いますが、ActionはReducerへ送信されるデータ本体でAction CreaterはActionを生成する関数です。この2つを混同しないように注意してください。Action Creatorを実行するとActionが生成されるだけで、Actionはdispatchされません。Stateを変更したい場合は、Storeの dispatch
関数を実行する必要があります。Actionを生成して、それを特定のStoreインスタンスにdispatchするAction Creatorを Bound Action Creator と呼びます。
Action Creatorが現在のStateを読み込んだり、APIを読み込んだり、RouterのTransitionのような副作用を発生させる必要がある場合は、Actionの代わりにAsync Actionを返す必要があります。
type AsyncAction = any
Async Action はDispatching Functionに渡される値ですが、そのままではReducerで処理することができません。Async ActionはBase Dispatch
Functionに渡される前に、Middlewareで1つまたは複数のActionに変換されます。Async Actionは適用するMiddlewareごとに型が異なります。たいてい、Async Actionは、Promiseやthunkのような非同期処理を実行するものです。Async ActionはReducerにそのまま渡されません。Middlewareで、それらの処理が完了した後、Actionがdispatchされます。
type MiddlewareAPI = { dispatch: Dispatch, getState: () => State }
type Middleware = (api: MiddlewareAPI) => (next: Dispatch) => Dispatch
MiddlewareはDispatching Functionを関数合成して新しいDispatching Functionを返す高階関数(higher-order function)です。たいてい、それは Async ActionをActionに変換します。
Middlewareは関数合成(function composition)で生成されます。
これはログを出力やRoutingのような副作用の実行や非同期のAPIの呼び出しに対応するMiddlewareを合成することで、それぞれを(非同期の)Actionに変換する1つのMiddlewareにすることを可能にします。詳しくはapplyMiddleware(...middlewares)
を見てください。
type Store = {
dispatch: Dispatch
getState: () => State
subscribe: (listener: () => void) => () => void
replaceReducer: (reducer: Reducer) => void
}
Storeはアプリケーションのstate treeを保存するオブジェクトです。そのstate treeはReducerで構成されます。Reduxを使っているアプリケーションではStoreは1つのみ存在してるべきです。
dispatch(action)
は上記のBase Dispatch Functionです。getState()
はStoreの現在のStateを返します。subscribe(listener)
Stateが変化したときに実行される関数を登録します。replaceReducer(nextReducer)
はhot reloadingやcode splittingの実装に使うことができます。これを使うことはほとんどないと思います。詳しく知りたい場合はStore APIリファレンスを見てください。
type StoreCreator = (reducer: Reducer, preloadedState: ?State) => Store
Store CreatorはStoreを生成する関数です。Dispatching Functionのように、Base Store Creator(ReduxパッケージからexportされるcreateStore(reducer, preloadedState)
)とStore Enhancerで生成されるStore Creatorを区別する必要があります。
type StoreEnhancer = (next: StoreCreator) => StoreCreator
Store EnhancerはStore Creatorを関数合成して新しい改良されたStore Creatorを返す高階関数です。これはStoreとのやり取りを関数合成で変更できることがMiddlewareと似ています。
Store EnhancerはReactのhigher-order componentsとほとんど同じコンセプトです。だから、“component enhancers”と呼ばれることもあります。
Storeは(何らかのクラスの)インスタンスではなく関数を素のObjectにまとめたものなので、簡単に複製することができます。そして、元のStoreを変更することなく改善することができます。
compose
のドキュメントにこの例があります。
たいてい、Store Enhancerを書くことはありませんが、developer toolsで提供されているStore Enhancerを使うことはあるかもしれません。それはアプリケーションに影響を与えずにtime travelを可能にします。面白いことに、Redux middlewareの実装はそれ自体がStore Enhancerです。
Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)
Copyright (c) 2021 38elements
The MIT License (MIT)
Copyright (c) 2015-present Dan Abramov
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.