Edited at

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

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


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

ユーザーが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する

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へ渡す

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を返すような関数でなければなりません。



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で処理するようにします。

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が保存する



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の代わりとなるのか