Reduxの基本的な構造の解説


まえがき

ReactでState管理がしたくなったのでReduxを導入することにしました。
Qiitaの記事だけだと理解が難しかったので、チュートリアルをちゃんとやって勉強しました。

この記事はRedux単体のものです。
React+Reduxに関してはこちら

Reduxとは何だ?

Reduxは状態管理コンテナです。
Reactとセットで語られるのをよく聞きますが、これ単体で機能するパッケージです。

ある程度の規模のソフトウェアを書いたことがある人は状態管理の難しさはよく分かるものと思います。

ReduxはState(状態) を 以下の部品を使っていい感じに管理します。

  • Action
  • Reducer
  • Store

Stateの定義

State(状態)の表現方法は様々です。
Reduxにおいてはアプリケーション内の全での状態を1つのオブジェクトに格納するという考え方をします。

TODOリストのサンプルコードをみるとわかりやすいと思います。
入れ子になった木構造をイメージしてください。

{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    {
      text: 'Consider using Redux',
      completed: true
    },
    {
      text: 'Keep all state in a single tree',
      completed: false
    }
  ]
}

Stateの管理

主要な流れです。

Actionの内容に応じてReducerがStateを変化させる仕組みです。
StoreStateReducer、その他諸々をパッケージしていい感じにオブジェクト指向にしてくれています。

要するにステートマシンを簡単に実装する機構だと思えばわかりやすいかもしれません。

Action

ActionStateに変更をもたらす通知データです。
Storeにdispatchすること適用することができます。

ここが大事なのですが、フォーマットが決まっています。

  • PlainObjectである必要がある。1
  • キーにtypeが存在する必要がある
  • typeの値は文字列推奨
{
    type: 'some string'
    ...
}

端的に言うとtypeラベルが付いた軽量なオブジェクトを用意しろということです。

Reducer

ReducerStateに変更をもたらす処理です。
ActionStateを読み込んで次のStateを出力する関数を定義します。

こちらもやはり、関数仕様が決まっています。

  • アプリケーション全体で一つだけ定義する
  • 純粋関数である2
  • 引数がActionと現在State
  • 返り値が更新State
  • 非サポートのアクションはstate変化を変えないことが推奨
function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    default:
      return state
  }
}

特に純粋関数で有ることは極めて重要です。
ここでAPI呼び出したり、内部状態を読み書きしたりしたら状態管理もなにもないので、実装するときは特に気をつけないといけないところです。

初期Stateもここで定義できます。

Store

ReducerとStateをまとめてインターフェイスを提供します。

提供するインターフェイスは以下

メソッド 役割
getState() Stateを取得する
dispatch(action) Stateを更新する
subscribe(listener) State変更時のコールバックを登録する

作成には専用の関数使用します。
このときにも初期Stateを設定することができます。

import { createStore } from 'redux'
import todoApp from './reducers'
const store = createStore(todoApp)

補助要素

ActionCreator

Actionを生成する関数の総称です。
書くとわかりますが、ActionはPlainObjectである必要があるので作るのが結構手間です。
アプリケーションのオブジェクトからActionを作るのが主な用途と思われます。

Splitting Reducer

Reducer自体はアプリケーション全体でひとつなのですが処理の一部を切り出して小さいReducerを作る事ができます。
これはActionの特定のtypeStateの特定の子要素にのみ作用するときに有効です。(そしてそうなるように設計するのが望ましい)

そうやって子Reducerに処理を委譲していくと根Reducerは子Reducerに処理を移譲して結果をつなぐだけになります。
そういう場合にcombineReducersという関数を使えば省略して表現することができます。
サンプルコードで結構目にする関数なのでギョッとしますがReducerを合成しているだけです。

まとめ

  • Reduxは状態管理コンテナ
  • Actionを受け取ってReducerStateを変化させる
  • ReducerStateをパックしてインターフェイスを提供しているのがStore

  1. 関数やクラスを含むなということです。 

  2. 入力によってのみ返り値が決まる関数。外部変数参照や内部状態を持たない。