redux

Reduxの基礎知識① Action,Reducers,Store【初心者向け】

Reduxとは

Redux(リダックス)とはReactの状態(State)を管理するためのライブラリであり、Flux(フラックス)のデータフローをベースとしています。(Reactについての記事は「Reactの基礎知識」)

公式サイトはこちらです。

Flux

Fluxとは、MVCモデルが双方向バインディングではなく、単一方向に流れていくような設計パターンです。

flux

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())

 

Reduxの基礎知識② Reactへの実装まで