Help us understand the problem. What is going on with this article?

Redux入門【ダイジェスト版】10分で理解するReduxの基礎

More than 1 year has passed since last update.

ReduxのGithubドキュメントを基に入門用記事として書いたものを、簡潔にまとめました。
もと記事はこちらです。

Reduxとは

Reduxは、ReactJSが扱うUIのstate(状態)を管理をするためのフレームワークです。Reactではstateの管理するデータフローにFluxを提案していますが、ReduxはFluxの概念を拡張してより扱いやすく設計されています。
Reduxはstateを管理するためのライブラリーなので、React以外にもAngularJSやjQueryなどと併せて使用することもできますが、Reactと使用するのが一番相性がいいです。

Reduxの要素

Reduxの要素と、それらの相関図です。

  • Action
  • ActionCreator
  • Store
  • State
  • Reducer

スクリーンショット 2015-11-30 1.13.59.png

Todoアプリを例にしてReduxのデータフローをたどりながら、これらの要素を説明していきます。

1. ユーザーの入力から、アクションを作成する

スクリーンショット 2015-11-30 1.27.09.png

ユーザーがtodoのテキストを入力して追加ボタンを押した場合を想定します。
ActionCreatorのメソッドに入力内容が渡されて、Actionのオブジェクトを作成されます。

Action

Actionは「何をする」という情報を持ったオブジェクトです。Actionはtypeプロパティを必ず持つ必要があります。

{
  type: 'ADD_TODO',
  text: 'Build my first Redux app'
}

ActionCreator

ActionCreatorはActionを作成するメソッドです。
FluxのActionCreatorとは違い、Actionを作るのみを行いStoreへのdispatchは行ないません。

function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

2. StoreへActionをdispatchする

スクリーンショット 2015-11-22 23.33.31.png

ActionCreatorで生成されたActionをStoreに送ります。
Storeのインスタンスにdispatch(action)を行なう事でStoreへ変更を伝えます。

dispatch(addTodo(text))

Store

Storeはアプリケーションの状態(state)を保持している場所です。
Storeはアプリケーション内で一つ存在し、一つのstateを保持しています。
Storesの役割は、

  • stateを保持する
  • stateへアクセスするためのgetState()を提供する
  • stateを更新するためのdispatch(action)を提供する
  • リスナーを登録するためのsubscribe(listener)を提供する

State

アプリケーションでの状態を表します。
このTodoアプリでは「現在選択されている表示/非表示」、「todoのリスト」をstateとして保持します。
より内容が複雑になる場合は、ネストしたツリー状の構造で表します。

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

3. dispatchされたactionとstateをReducerへ渡す

スクリーンショット 2015-12-04 3.06.44.png

Storeは、Storeを作成する際にStateを変更するためのメソッドであるReducerを一つ登録します。
Storeはdispatchされると、引数のactionと現在保持しているStateをReducerへ渡し、新しいStateを作成します。

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

Reducer

reducerはactionとstateから、新しいstateを作成して返すメソッドです。ポイントは、引数のstateを更新することはせず、新しいstateのオブジェクトを作成して返します。
reducerのメソッドは副作用を起こさないpureな関数でなければならず、Aというstateに対して毎回必ずBというstateを返すような関数でなければなりません。

スクリーンショット 2015-11-22 23.33.49.png
Reducerの実装は、actionのtypeに応じて処理を書く事になります。

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    case ADD_TODO:
      return Object.assign({}, state, {
        todos: [
          ...state.todos,
          {
            text: action.text,
            completed: false
          }
        ]
      })
    case COMPLETE_TODO:
      return Object.assign({}, state, {
        todos: [
        ...state.todos.slice(0, action.index),
        Object.assign({}, state.todos[action.index],{
        completed: true
      }),
      ...state.todos.slice(action.index + 1)
    ]
  }) 
    default:
      return state
  }
}

Reducerはアプリケーションが大きくなるにつれて実装が肥大化してしまうので、Reducer内に子Reducerを作成し、stateのプロパティごとに子Reducerで処理するようにします。
スクリーンショット 2015-11-22 23.34.10.png

function todos(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case COMPLETE_TODO:
      return [
        ...state.slice(0, action.index),
        Object.assign({}, state[action.index], {
          completed: true
        }),
        ...state.slice(action.index + 1)
      ]
    default:
      return state
  }
}

function visibilityFilter(state = SHOW_ALL, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return action.filter
    default:
      return state
  }
}

function todoApp(state = {}, action) {
  return {
    visibilityFilter: visibilityFilter(state.visibilityFilter, action),
    todos: todos(state.todos, action)
  }
}

4. Reducerが作成した新しいstateをstoreが保存する

スクリーンショット 2015-12-05 0.50.34.png
Reducerによって新しいStateが作成されるので、Storeは現在のStateに代わり保持します。

Reactとの連携

相関図のViewの部分がReactJSが担う部分となります。
実装については「Redux入門 6日目 ReduxとReactの連携」を参照してください。

Reduxの3原則

最後になりますが、Reduxの基本設計は以下の3つの原則に基づいて設計されています。
上記のデータフローはこの原則に則っていることがよく分かると思います。

1. Single source of truth

アプリケーション内でStoreは1つのみとし、Stateは単独のオブジェクトとしてStoreに保持される。

2. State is read-only

Stateを直接変更することはできず、actionをStoreへdispatchすることでしかStateは変更できない。

3. Mutations are written as pure functions

Stateを変更する関数(Reducer)はpureな関数にする。

最後に

以上がReduxの基礎となります。詳しく掘り下げたい場合は以下の記事を参考にしてください。

2018/4/25 追記
React本体が提供するReduxライクな機能であるContextAPIについて書きました。
* ReactのContext APIはReduxの代わりとなるのか

kitagawamac
フロントエンドエンジニア speakerdeck https://speakerdeck.com/kitagawa312
toreta
飲食店向け予約/顧客台帳サービス「トレタ」、超直前予約アプリ「トレタnow」を開発・提供するスタートアップ企業です。
https://corp.toreta.in
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした