目次
Reduxとは
Redux(リダックス)とはReactの状態(State)を管理するためのライブラリであり、Flux(フラックス)のデータフローをベースとしています。(Reactについての記事は「Reactの基礎知識」)
公式サイトはこちらです。
Flux
Fluxとは、MVCモデルが双方向バインディングではなく、単一方向に流れていくような設計パターンです。
MVCモデルで言うと、Model(データやロジック)がStore、View(表示)がView、Controller(命令)をActionとDispatcherに相当します。
Action(どうしたいか)を発行して、Dispatcherがそれを検知しStoreを更新、そしてViewが描画されると言う流れです。
これ以上の詳しい説明は省きますが、データフローが単一方向になることで理解しやすく、また状態を管理しやすくなります。
Reactはツリー構造のコンポーネントで構成されているため、SFCなどを実装するとPropsで親から子へとずっと渡していかなければなりませんでした。これでは密結合になってしまい、何か修正を加える時にも大変になってしまいます。また、デバッグも簡単には行うことができませんでした。
そこでReduxを導入して状態(state)を別の場所で管理することでコンポーネントが綺麗になり、これらの問題を解決することができます。
Reduxは以下のコマンドでインストールできます
$ npm install --save redux
Reactでは、アプリケーションの状態を以下のようなオブジェクトで表現します。
{ todos: [{ text: 'Eat food', completed: true }, { text: 'Exercise', completed: false }], visibilityFilter: 'SHOW_COMPLETED' }
もちろんネストすることも可能です。
この状態オブジェクトに対して変更を行いたいときにActionsを発行し、Reducers(Dispatcherのようなもの)でStoreを更新するような流れです。
Reduxには、以下の3つの原則があります。基本的にはこれらを守るように実装して行きましょう。
- Single source of truth (信頼の一つの情報源)
- State is read-only (状態は読み取り専用)
- Changes are made with pure functions (変更は純粋な関数によって行われる)
Single source of truth
状態はシングルツリー構造にすることによって、信頼性が高まり、デバッグやアプリケーションの動作の予測が容易になります。
ちょうど上に載せた例のような感じです。
State is read-only
状態を変更するのは、アクションを発行したときのみとします。これにより中央集権的に管理することが可能となり、デバッグが容易となります。
Changes are made with pure functions
ここで言う関数とはReducersのことで、状態の変更を統一するために、Reducersは外部要因に作用されない単純な関数として実装するべきです。
この後は、Actions、Reducers、Storeについて詳しく見ていきたいと思います。
Actions
ActionsはReactの状態を更新することのできる唯一の方法であり、以下のように定義されます。
{ type: 'ADD_TODO', text: 'Go to swimming pool' } { type: 'TOGGLE_TODO', index: 1 } { type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }
何をしたいのかと言う意図を表すのがtypeであり、他にも自由に要素を持つことができます。
例えば一番上の例であれば”Go to swimming pool”と言うTODOをADDしたい、と言うような意図と解釈できます。
Action Creators
Action Creatorsとは読んで字の如くなのですがActionsを生成するためのメソッドのことです。これを実行することで特定のActionsが生成され返却されます。
function addTodo(text) { return { type: ADD_TODO, text } }
これは先ほどの例に出てきたActionsを返すAction Creatorsです(textは任意になっていますが)
dispatch()メソッドでStoreにActionsを渡せるのですが、dispatchまでをメソッドにする方法もあります。
dispatch(addTodo(text)) //addTodoで返ってきたActionsをdispatchでStoreに送っている const boundAddTodo = text => dispatch(addTodo(text)) //dispatchまでメソッドにしている boundAddTodo(text)
Reducers
Reducersは、Actionsを受け取ってStateをどのように変化させるかを決定する関数です。
Stateを変更すると言っても直接書き換えるのではなく、渡されてきたStateから新しいStateを作成しています。
イメージとしては下のような感じです。
(previousState, action) => newState
もちろん最初はStateを持たないので、ReducersではInitialStateでStateの初期状態を割り当てています。
以下がReducersの例です。
const initialState = { todos: [] } function todoApp(state = initialState, action) { switch (action.type) { case case ADD_TODO: return Object.assign({}, state, { todos: [ ...state.todos, { text: action.text, completed: false } ] }) default: return state } }
switch文でActionsのtypeを判断し、処理を行います。
もし該当するtypeがなければ、Stateを変更してはいけないのでdefaultで今のStateをそのまま返すようにします。
Reducersでは直接Stateを書き換えてはいけないので、Object.assignでは最初に空のオブジェクト{}を渡して、Stateをコピーするようにしています。
また、もしアプリケーションが大規模になり、複数のReducersを持つようになった場合は、combineReducersを使うことで簡単にReducersをまとめることができます。
import { combineReducers } from 'redux' const todoApp = combineReducers({ visibilityFilter, todos }) export default todoApp
Store
Storeはアプリケーションの状態(state)を持っている部分です。3原則にもあったように、Storeは1つだけ持つようにします。
Stateの初期値は先ほどReducersで書きましたが、生成は以下のように行います。
import { createStore } from 'redux' import todoApp from './reducers' const store = createStore(todoApp) // 初期値を指定したいとき const store = createStore(todoApp, window.STATE_FROM_SERVER)
todoAppは先ほどcombineReducersで作成したReducersです。
stateはgetState()メソッドで取り出すことができ、現在のstateを見たいときは次のようになります。
console.log(store.getState())