Mitomex Blog

Redux について(概念編)

2020-05-20

Redux

Redux と聞くと「何か難しそうなもの」というイメージを持ってしまう人も多いかと思います。 実際に僕もそうでした。

そこで改めて Redux について自分なりにわかりやすくまとめてみました。 2つの記事に分けて Redux について説明していきます。

まずはこの記事で Redux の概要についてまとめ、次の記事で React を使って Redux を実際のコードではどのように記述していくのかについてまとめたいと思います。

イメージしやすくするために、Todo アプリを例に使って説明していきます。

Redux とは

Redux とは State を管理するものです。

Todo アプリでいうとそれぞれのタスクが State になります。

  • タスクを追加
  • 終わったタスクを完了にする
  • いらないタスクを削除する

アプリでは上記のようなことが必要になってきます。

このタスクの変更のしかたをルール化してコードを読みやすくしてくれるのが Redux になります。

Redux の概念には

  • State
  • Action
  • Reducer
  • Store

という4つの言葉が出てきます。

それぞれの言葉と役割を把握することが Redux の概念を理解するのに役立つので、ひとつひとつ順番にみていきましょう。

State とは

State は、よく「状態」と訳されることが多いですが、「状態」と言われてもいまいちピンときませんでした。

そこで State を英英訳してみると、こうありました。

誰かまたは何かが特定の時間にある特定の状態。

この「特定の時間にある特定の状態」が State というとなんとなくイメージしやすいかなっと思います。

アプリはこの「特定の状態」を変化させて使っていくものです。

Todo アプリで「筋トレをする」というタスクが終わったのでチェックをいれる、としたとします。

todo before after

1番目の状態( State )から2番目の状態( State )に変化しました。

この状態の変化を管理する必要があり、この管理を扱うのが Redux です。

上の状態変化を Object で表してみると、

{
  todos: [
    {
      text: "筋トレをする",
      completed: false
    },
    {
      text: "買物に行く",
      completed: false
    }
  ]
}

↑ これが1番目の状態。

{
  todos: [
    {
      text: "筋トレをする",
      completed: true // true になった
    },
    {
      text: "買物に行く",
      completed: false
    }
  ]
}

↑ これが2番目の状態。

となります。

このように State は ひとつの Object で表現します。

Action とは

Todo アプリでは

  • タスクを追加したい
  • タスクを完了にしたい

など、やりたいことがあります。

このやりたいことを決める・定義するのが Action になります。

Action は Object で表現します。

「タスクを追加する」という Action の場合は、

{
  type: "ADD_TODO"
}

となります。

type プロパティの値に、やりたい Action を記述します。 type に指定する文字列は自分で好きなように決めて大丈夫です。 分かりやすい文字列で指定しましょう。

ただこれだけだと何のタスクを追加したいのかわからないので、 以下のように追加したいタスクも合わせて表現します。

{
  type: "ADD_TODO",
  text: "買物に行く"
}

こんな形になります。

Action では type は必須です。 やりたいことを表現するので当然ですね。

2つ目以降のプロパティには「やりたいこと」に必要な情報を追加していきます。

「タスクを追加する」の場合は追加したいタスク、text: "買物に行く" が必要になるので追加します。

別の Action 、例えば「1番目のタスクを完了にする」の場合は、

{
  type: "COMPLETE_TODO",
  index: 1
}

となります。

タスクを完了にする場合は、どのタスクを完了にしたいかという情報が必要になるので、 index: 1 を追加しています。

Action は、あくまでやりたいことを定義しているだけです。

Action に ADD_TODOCOMPLETE_TODO しかなければ、 その他の Action は、このアプリではできないということになります。

もしタスクの変更をしたい場合、「変更」という Action がないのでできません。 このアプリで変更したい場合は、一度削除して新しいタスクを追加するということになります。

Action Creator

定義した Action を使うときには、Action Creator という Action をつくる関数を使います。

const addTodo = (text) => {
  return {
    type: "ADD_TODO",
    text
  }
};

こんな形になります。Action の Object を返す関数です。まさに Action を Create する関数です。

繰り返しになりますが、Action はあくまでやりたいことを定義しているだけです。

今まで説明してきた State と Action だけでは何も変化しません。

Reducer とは

実際に「状態」を変化させるのが Reducer になります。

Reduce は「〜をある状態に変える」という意味があります。

Reducer は今の状態( State )とやりたいこと( Action )を受け取って新しい状態を返す関数です。

Reducer の役割

この図を Object で表現すると

{
  todos: [
    {
      text: "筋トレをする",
      completed: false
    }
  ]
}

という今の状態があって、

{
  type: "ADD_TODO",
  text: "買物に行く"
}

という Action がしたい。その結果の状態が

{
  todos: [
    {
      text: "筋トレをする",
      completed: false
    },
    {
      text: "買物に行く",
      completed: false
    }
  ]
}

となるようにしたいわけです。

必要な変更は、最初の状態( Object の配列 )に新しいタスクの Object を追加することです。

これを関数にすると

const todos = (state, action) => {
  return {
    ...state,
    todos: [
      ...state.todos,
      {
        text: action.text,
        completed: false
      }
    ]
  };
};

元々の状態( state )に新しいタスク( { text: action.text } )を追加した配列を返す関数です。

action の text にタスク名が入っているので、 action.text は 買物に行く になります。

実際のアプリでは Action が複数あることが多いので、実装する Reducer は action の type ごとにそれぞれの関数を用意する必要があるので、 以下のように Switch 文で分岐させて表現します。

const todos = (state, action) => {
  switch (action.type) {
    case "ADD_TODO":
      return {
        ...state,
        todos: [
          ...state.todos,
          {
            text: action.text,
            completed: false
          }
        ]
      };
    case "COMPLETE_TODO":
      return // ここにタスクが完了になった新しい State を返す関数を記述する
    default:
      return state;
  }
};

Reducer は State の変更のしかたを定義しているだけです。

Store とは

Store は、今までみてきた StateActionReducer の3つをまとめるものになります。

Store には5つの役割があります。

  1. State を持っている
  2. State を取得できるようにする( getState() メソッド )
  3. State を更新できるようにする( dispatch() メソッド )
  4. State の変化を監視できるようにする( subscribe() メソッド )
  5. State の変化の監視を解除できるようにする( subscribe() メソッドの返り値 )

ちなみに subscribe を訳すと「事前に支払うことにより定期的に何か、通常は出版物を受け取るように手配する」となります。 ここでは最新のデータが届くように登録する、というイメージですね。

Store を使うことによって今まで別々にあった StateActionReducer が機能します。

Todo アプリを使うときの流れでイメージしてみると以下のようになります。

  1. View で現在のタスク一覧を表示させたいので store.getState() で使って State を取得
  2. タスクを追加したいので View で store.dispatch(addTodo('買い物に行く')) を使って変更したいことを Store に伝える
  3. dispatch を受け取った StoreReducer を使って State をどのように変更するのかをみて State を更新

これで State を変更するための機能がすべて揃いました。

まとめ

このように Redux では

  1. State を扱う
  2. Action にやりたいことを書く
  3. Reducer に State の変更のしかたを書く

とルールを決めて State を管理しています。

Redux の概念を図で表すとこうなります。

Redux Core Concept

この Redux の概要をふまえて実際のコードではどのように書いていくのか、次の記事でまとめていきたいと思います。

Reduxについて(実践編)

参考ページ: Core Concepts | Redux