##Reduxとは
『redux』とは、フロントエンドにおける状態管理に特化したライブラリ。
##react の state の限界
小規模なアプリケーションであれば、もちろん1つの『state』でも十分に管理できるが、大きなアプリケーションになると、コンポーネントツリーは肥大化し、状態管理が複雑になる。
もし、親コンポーネントで状態を一限管理しようとするととても大きな『state』になってしまい、状態の更新や取得が複雑になりバグの原因となる。
また、バケツリレーが至るところで起こってしまうのも管理が大変になる。
また、それぞれのコンポーネントで独自の『state』を持ってしまうと、他のコンポーネントにその状態を知らせるのが難しくなり複雑化する。
##redux で状態を分離する
『redux』を使うことで状態管理にかかる部分をコンポーネントのツリーから分離することができる。煩雑なバケツリレーからの解放(完全に解放されるわけではないが幾分ましになる)やコンポネート間での状態のやりとりが可能になる。
##redux の特徴
『redux』は『flux』という概念に基づいて設計されている。『flux』とは一方向に状態が流れるというという特徴を持っている。また、値を直接変更しないという関数型の特徴も併せ持っている。
##Redux の用語解説
####action type
reducer や middlware(後述)がどの処理を行うかを判断するために使われる。
直接値を変更できないのでこのような方法が取られる。定数や typescript であれば enum で定義されることが多い。
const ACTION_TYPE = "ACTION_TYPE";
####action
「どんな処理をするのか(= action type)」と「それに必要な引数(= payload)」が入ったオブジェクトを返す関数。view 側からこの action を dispatch(= redux に通知)することで redux のフローが実行されるという仕組み。
const actionName = payload => ({
type: ACTION_TYPE
payload
});
#####※action は純粋関数である必要があり、非同期処理などの副作用がある処理を行ってはいけない。
正確には、『action type』と『payload』を含むオブジェクトが『action』 で、その 『action』オブジェクトを返す関数は『action creator』と呼ばれている。毎回、同じようなオブジェクトを定義するのが煩雑なのでこのような記述方法が一般的。ライブラリ等を使ってもっと簡単に実装する方法もあるが、実装の意味を理解するためにもあえてフルスクラッチで実装している。
####reducer
payload と直前の state を受け取って次の state を返す関数で、redux におけるstoreは reducer を介さないと更新できない仕組みになっている。どのように状態を更新するかは action type で条件分岐をするのが一般的。
const reducer = (state = initState, { type, payload }) => {
switch (type) {
case ACTION_TYPE:
return newState(payload);
default:
return state;
}
};
####middleware
基本的に非同期の処理(詳細には副作用のある処理)を行うところで、redux 自体には非同期を処理する機能は備わっていないので別のライブラリ(redux-thunk, redux-saga, redux-observable など)を使うのが一般的。
『middeware』の関数はその内部で他の dispatch を行うことができる。よくある使われ方としては、非同期処理が解決されるまで待って解決されたらその値を dispatch するという使い方で、サーバーからのデータの取得や更新などで頻繁に行われる。
view から直接 middleware の関数を呼んだり、middlreware の処理を発火させるための action を実行する。この呼び出し方は使うライブラリによって異なる。
####store
コードで直接的に記述する部分はほとんどないが、redux の状態そのものは store と呼ばれるオブジェクトで、この中に各 reducer に対応した state がネストして入っている。reducer を介してしか変更のできない immutable なグローバル変数。
グローバル変数で状態を管理すると色々なところから変更ができて大変なことになるが、
それを immutable という制約で解決しているのが redux。redux のアーキテクチャに乗っ取ることで安全にグローバルスコープの変数が使えるようになる。
##react-redux
『redux』は実は react のためだけのライブラリではなく、redux 自体は react に対するインターフェイスを持っていない。redux の react に対するインターフェイスを提供するのが react-redux というライブラリである。『react-redux』 は『Provider』と『connect』という関数を持っている。
####Provider
react がどの store を使うのかを定義するためのもので、多くの場合はルート階層のコンポーネントをこの『Provider』でラップして、それより下のコンポーネントでその store が使えるようにする。store は単に状態が保管されている場所ぐらいの認識でOK。
重要なポイントは『Provider』でラップしただけではコンポーネントは store が持っている状態について知ることができないし redux に対して何もできないということ。
下記で、説明するconnectを使うことで初めてコンポーネントは store から必要なデータを取得したり状態の更新を redux に対して要求(= dispatch)することができるようになる。
####connect
各コンポーネント単位でどの state を参照してどの dispatch を実行するのかを定義するための関数。
高階関数になっていて、適用したいコンポーネントとどのデータを props として渡すのかを定義した関数を引数にとる。
const mapStateProps = ({ count }) => ({ count });
const mapDispatchProps = dispatch => ({
increment: count => dispatch(increment(count)),
decrement: count => dispatch(decrement(count))
});
このように書けば props に redux の情報を渡すことができるという理解でOK。
##なぜProviderとconnectがあるのか
####保守性が低くなる
これは、state のスコープが限定されないために起こりうる問題で、どこからでも簡単に参照できてしまうので、どこか他のところを変更したときにどこまで影響が及ぶのかが予測しづらくなる。
もし『connect』があると、state が参照できるスコープを制限することができるので、被害も最小限に抑えられる。
####バケツリレーが煩雑になる
どこでも、参照できるとは言っても結局は『Provider』でラップされたそのコンポーネントしか状態について知ることはできないので、それ以下のコンポーネントに関してはバケツリレーをしていくしか解決法がない。
これでは、そもそも redux を使わなかったパターンと変わらないので、やはりconnectは必要である。
##参考サイト
[React/ReduxでGoogleカレンダー風カレンダーアプリケーションを作ろう]
(https://www.techpit.jp/courses/22/curriculums/23/sections/197/parts/696)