Reduxについての概要
以前からReact/React NativeにてReduxを利用したアプリを作成しています。
Reduxの概念について説明する機会があったので、簡単にですがまとめておきます。 いったんキーワードベースでの解説としいます。サンプルコード等は少なくしました。
基本的にReduxはstateの管理を1つのストアで集約的に管理するものです。 これを使うことでコンポーネントから一元管理されたstateへアクセスできるようになります。 使わない場合はprops等でやり取りすることになりますね。
概要
- ドキュメント
- Store
- Actions
- Reducer
- Connect
ドキュメント
https://redux.js.org/introduction/getting-started
reduxのドキュメントです。基本これのチュートリアルを順番に読んでいくことで理解が進みます。
https://react-redux.js.org/introduction/basic-tutorial
reduxをreactで使うためのライブラリーです。reduxはreact以外のフレームワークでも使えるようで、それぞれに対応したライブラリーを使うことがおすすめされています。
reactに対してはこのreact-redux
が公式でもオススメとされています。
Store
ReduxではアプリケーションのすべてのStateをひとつのオブジェクト「Store」にて管理する。
StoreはgetStoreによりデータにアクセスが可能で、dispatchによってデータの更新を行う。 subscribeによってリスナーを登録することもでき、解除もできる。
データを分割したい場合、Storeを複数つくるのではなくReducerの分割を行います。
アプリケーションに対してStoreは1つという決まり。
このStoreにてデータを一元管理すること・できることがReduxの肝心なところ。
Actions
Storeに情報を渡すためのオブジェクト。
オブジェクトでありプロパティとして「type」が必要。 typeの値には定数文字列が推奨されており、文字列リテラルでも良い。 「ActionTypes」としてファイルを切り出して定義しておくのがオススメ。
payloadとしてストアに渡されるデータをまとめて定義することもある。
// Action 純粋なオブジェクト
const addTodoAction = {
type: ADD_TODO,
payload: {
id: ++nextTodoId,
content
}
}
;
ActionCreator
引数を受け取りActionを返すメソッド。 これを「dispatch」でStoreに渡すことでStoreの更新を行う。
// これは引数を受けとり、Actionを返すのでActionCreator
export const addTodo = content => ({
type: ADD_TODO,
payload: {
id: ++nextTodoId,
content
}
});
Reducer
Storeへ渡されるActionに応じて、State更新の内容を定義しておく。
Actionの「type」によって更新内容を切り分ける。
Reducerは前のStateとActionを受け取り、次のstateを返す。 Stateに書き込むのではなく別のオブジェクトを返す。したがって、Stateを直接書き換えてはいけない。 前のStateの中身を展開したものと、更新するパラメータをオブジェクトにしてまとめて返す。すなわち更新後のState。
意図せず未定義のアクションが渡された場合は前のStateをそのまま返す。
一番最初はStateが空なはずなので、Stateの初期値を与えておく。
Reducerでやってはいけない3つのこと
- 引数に与えられたものを直接書き換えてはいけない
書き換えるのではなく、新しいStateをつくって返す - APIの呼び出しやルーティング処理をしてはいけない
- 純粋でない関数を呼んではいけない
dateとかrandomとか
CombineReducer
データ定義や関連するドメインなどで分割して子Reducerを作っておき、合成して親Reducerとして扱うと良い。 Stateの一部を管理するような子Reducerを複数作成して、CombineReducerにて結合するのが良い。
動作としてはDispatchにActionを渡すと、全ての子ReducerにActionを渡して帰ってきた新しいStateを結合しているらしい?。
export default combineReducers({ todos, visibilityFilter });
Connect
Connectを使ってコンポーネントをラップすることでコンテナ化する。 こうしてコンテナ化することで、Storeからのデータ取得やdispatchによるデータ更新を可能にする。
export default connect(
mapStateToProps,
mapDispatchToProps
)(Component);
mapStateToProps
Connectの1つ目の引数。
Storeにアクセスしてデータを取得する処理を行い、stateをコンポーネントのpropsに渡します。
Storeから必要なデータを取得・変換・編集してコンポーネントのpropsへ渡すことが目的です。
基本的にReduxはStoreに何らかの更新があると、データが再取得され再レンダリングの対象になるようです。
このメソッドを定義しなかった場合はStoreがコンポーネントのpropsに渡される?ようです。 nullとかを渡せば、更新不要なコンポーネントとして定義できるかな。
mapDispatchToProps
Connectの2つ目の引数。
Storeにdispatchして更新するための処理をコンポーネントのpropsに渡します。
コンポーネント内でdispatchを実行してもいいのですが、こちらで定義しておくことも出来ます。 定義しなかった場合はdispatchがコンポーネントに渡されるので、コンポーネント内でpropsのdispatchを実行します。
おわりに
Reduxは理解するのに時間がかかるという話を聞きますが、それほどで難しいものではないです。 時間を取ってドキュメントを見れば簡単に理解できます。大きなフレームワークではなく小さなライブラリーですので。
ドキュメントが英語なのでとっつきにくいですが、勉強してみるとおもしろい仕組みです。 コンポーネント間で正しくデータを管理して渡せば、Reduxが無くても実装は可能です。しかし、Reduxを選択肢として使えるととても便利です。
React・React Nativeで実装するに当たって、主要ライブラリの1つですのでしっかり身につけておきたいです。