アプリケーションの状態管理のフレームワーク「Redux」をご存知ですか?

アプリケーショ開発の分野では非常に支持されているフレームワークですが、理解に苦しんでいる方も多いはず。

そこで今回は、Reduxの考え方をシンプルに紐解いていきたいと思います。

この記事を読めば、Reduxが理解できずにモヤモヤしている方もきっとスッキリできるはず。それでは解説していきます!

Reduxとは?

Reduxとは、Reduxとは、React.jsで使用するstateつまりアプリケーションの状態を管理するフレームワーク(考え方)です。

以下のような概念でアプリケーションのステートを管理します。

Redux概念図
  • State:アプリケーションの状態
  • Action:ユーザーが何押したいかという情報を持つオブジェクト
  • Reducer:Actionを元にStateを更新するメソッド
  • Store:Stateの情報を保持している場所

ユーザーがコンピューターに対して何らかの操作をしたときに、アプリケーションのステートは更新されるのですが、そのステートがどんな状態になっているかを記録しておく必要があります。

それがReduxの役割です。

Reduxを構成する要素

前述でも触れましたが、Reduxはデータを扱う考え方の一つです。Reduxでは、アプリケーションで扱うデータ全てを、1つの変数(stateと呼びます)で管理します。

その、ステート(state)を管理するために、ストア( store)というものがあり、ステートを変更する際に、アクション(action)、ディスパッチ(dispatch)、リデューサー(Reducer)、というものがあります。ステートの変更を受け取るために、サブスクライブ(subscribe)という機能もあります。急にいっぱい出てきて混乱しますが、現時点では理解していなくて問題ありません。

Reduxの全体像

state

構成要素を1つずつ見ていきたいと思います。まずはステート(state)です。
ステート(state)は、アプリケーションで扱う全データを保持するオブジェクトです。大きな1つのJSONと考えるわかりやすいかもしれません。
ここでは、タイトル(title)とカウント(count)を保持するオブジェクトとしましょう。

このとき、stateは以下のように記述します・

// ステート
{
    title : '',
    count : 0
}

ストア(store)

上記のステートを管理するものとして、storeを定義します。Reduxの全体像のイメージで示した通り、ストア(store)がステート(state)を保持します(Store has State)。

// ストア
const store = {

    // ストアが、ステートを、保持している.
    state : {
        title : '',
        count : 0
    }
}

ストアの中に、ステートがあります。これもわかりやすいですね、簡単なJavaScriptです。
Reduxでは、ストアがステートを保持していて、ステートの変更はストアが行います。

アクション(Action)

ストアに変更したい内容を伝えるため、アクション(action)を作成します。アクションは変更指示書のようなものです。

// アクション.
const action = {
    type : 'ADD_COUNT'
}

アクションも簡単なJavaScriptオブジェクトですね!
アクションにはtypeという決まった項目を用意し、その項目に指示を書きます。ここではADD_COUNTという文字列を指定しました。

ディスパッチ(dispatch)

指示書を作っても、それがストアに届かなければ意味がありません。ストアに届けるために、ディスパッチ(dispatch)を使います。
ストア(store)にdispatchというメソッドを用意し、引数でアクションを受け取ります。

const store = {

    // 上記のステートを、ストアは保持している.
    state : {
        title : '',
        count : 0
    },

    // ディスパッチを追加.
    dispatch : function (action) {
        // あとで実装します.
    }
}

リデューサー(reducer)

ストアは受け取ったアクションを読み取って、ステートを変更します。アクションの内容を理解してステートを変更する処理を担当するのが、リデューサー(reducer)です。

ここではまず、リデューサー(reducer)の処理を定義したいと思います。
リデューサー(reducer)は、現在のステートとアクションを引数で受け取り、変更後の新しいステートを返却する関数として定義します。

// リデューサー(reducer).
// @param state 現在のステート
// @param action 変更内容
function myReducer(state, action) {

    // actionのタイプごとに、処理を分ける
    switch (action.type) {

        // ADD_COUNTの場合は、countを1増やす.
        case 'ADD_COUNT':
            state = {
                ...state,
                count : state.count + 1
            }
            return state

        default:
            return state
    }
}

リデューサーの中ではアクションのtype項目を見て、それぞれに応じた処理を行います。ここではtypeADD_COUNTの場合に、ステート内のcountに1加えています。

これで、アクション内容を理解してステートを変更する処理(=リデューサー)を作成できました。

なおReduxでは、既存のステートを変更するのではなく、新しいステートを毎回作成します。

// 新しいステートのオブジェクトを作成している.
state = {
    ...state,
    count : state.count + 1
}

これはReduxの3原則の一つ「State is read-only」でとても大切なことですが、最初のうちは気にしなくて良いと思います。まずは大枠を掴むことが大切です。

上記で作成したmyReducerを、storeに設定します。

// Store に reducer を追加.
const store = {

    state : {
        title : '',
        count : 0
    },

    // リデューサー.
    reducer : myReducer,

    dispatch : function (action) {
        // あとで実装します.
    }
}

これで、ストアがリデューサーを使えるようになりました。

実際にStateを変更する流れ

ここまでの解説を実装した状態で、下記のコードを実行します。

// アクションを定義.
const action = {
    type : 'ADD_COUNT'
}

// 動作テスト
store.dispatch(action)
console.log('1回目:', store.state)  // 1回目: {title: "", count: 1}

store.dispatch(action)
console.log('2回目:', store.state)  // 2回目: {title: "", count: 2}

store.dispatch(action)
console.log('3回目:', store.state)  // 3回目: {title: "", count: 3}

これで、アクション、ディスパッチ、リデューサー、を使って、ステートの値を変更できました。

実際にReduxのライブラリを使う場合には、store.stateとプロパティに直接アクセスするのではなく、store.getState()と関数を通してステートを取得します。
ここでもそれにならって、getStateメソッドを定義したいと思います。

// Store.
const store = {

    /* 省略 */

    // ステートを取得するメソッドを追加.
    getState : function () {
        return this.state
    }
}

上記を使用してステートを取得します。

Reduxのメリット

最後に、Reduxを導入するメリットを考えてみたいと思います。データ管理の方法が大変すぎると感じる方もいるのではないでしょうか?大きなプロダクトを開発する時には、下記3つのメリットが生きてきます。

  1. データの変更方法が制限されている
  2. データの変更を検知する術がある
  3. 離れたコンポーネント間でデータのやり取りができる

データの変更方法が制限されている

これは一見不便そうですが、プロダクトが大きくなるとメリットがあります。

データを変更する方法が限られるので、データの変更者がわからないという問題が発生した際に、すぐに調べて変更者を特定できます。データの更新方法が制限されていたり、データの更新者が制限されることで、アプリケーションのデータの堅牢性が上がります。

データの変更を検知する術がある

これはReduxの「サブスクライブ(subscribe)」という機能で可能です。

例えば、画面ヘッダーのコンポーネントで、「誰が更新したかはわからないけど、ステートの中のtitleが変わったら、自分の表示を更新しよう」といった実装ができるようになります。

変更を通知してくれる仕組みは非常に便利です。

離れたコンポーネント間でデータのやり取りができる

これは2つ目のメリットとも関連します。

例えばスレッドを表示する画面があり、スレッドが切り替わるたびに、スレッド名を画面ヘッダーに反映したいとします。スレッド画面はステートのtitleを更新すれば、画面ヘッダーがそれをサブスクライブしているから自動的に変更するといった使い方ができます。
特にReactなど、親子関係以外のコンポーネント間でデータをやり取りしたい場合に便利です。

他にもメリットはあると思いますが、Reduxを使うことで上記のメリットを享受できます。

まとめ

今回はReactで使用するReduxについて解説しました。内容をまとめると以下のようになります。

  • ReduxはReactアプリケーションのStateを管理するためのアプリケーション
  • Reduxを使用した管理は手間がかかるがその分メリットもある
  • Reduxを(大きなプロダクトで)使用するメリットは下記
    • データの変更方法が制限されている
    • データの変更を検知する術がある
    • 離れたコンポーネント間でデータのやり取りができる

プロダクトの規模に応じてステート管理にReduxの使用を検討してみてください。