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

Redux入門 3日目 Reduxの基本・Reducers(公式ドキュメント和訳)

More than 1 year has passed since last update.

前回 Redux入門 2日目 Reduxの基本・Actions

前回に引き続きTodoアプリの作成を通してReduxの要素の解説を行ないます。今回はReducerです。

2.2 Reducers

reducerは、actionを受けてstateを変更するの為のメソッドです

Designing the State Shap (Stateのデータ設計)

  • Reduxでは、stateはすべて個別のオブジェクトとして保持されます

今回のTodoアプリでは2つの内容をstateで保持します

  1. 現在選択されている表示/非表示

  2. todoのリスト

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

stateにはUIの内容を入れないようにするのが推奨されています

Handling Actions (actionの制御)

reducerは、現在のstateとactionを受けて新しいstateを返すだけの純粋なメソッドです。

(previousState, action) => newState

reducerの中で以下のことをやってはいけません

  • 引数のstate, actionインスタンスの値を変更する
  • 副作用をおこす(APIを呼んだり、ルーティングを変えるなどなど)
  • 毎回値が変わるもの(Date.now() や Math.random())を扱う

ここからReducerを書いていきます。
Reduxでは最初にreducerはstateがundefinedで呼び出します。その際に初期値を設定します。

import { VisibilityFilters } from './actions'

const initialState = {
  visibilityFilter: VisibilityFilters.SHOW_ALL,
  todos: []
}

function todoApp(state, action) {
  if (typeof state === 'undefined') {
    return initialState
  }

  // For now, don’t handle any actions
  // and just return the state given to us.
  return state
}

ES6ではもっと簡潔に記述できます

function todoApp(state = initialState, action) {
  // For now, don’t handle any actions
  // and just return the state given to us.
  return state
}

actionがSET_VISIBILITY_FILTERの時の処理を追加します

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    default:
      return state
  }
}
  • Object.assignを使用してstateのコピーを作成しています。Object.assign(state, { visibilityFilter: action.filter })とすると、stateを変更することになってしまうのでやってはいけません。空のオブジェクトにマージさせないといけません。

※ Object.assignはES6の仕様ですがまだ実装されているブラウザは多くありません。polyfillかBabelを使用してください

  • stateが不明の場合は、stateをそのまま返すのがセオリーです

reducerで制御するactionを追加します

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
  }
}
  • COMPLETE_TODOでは、stateを変更しないようにsliceを使って順番を変更した新しい配列を作成しています。

Splitting Reducers(Reducersを分割)

reducer内でのvisiblity_filterとtodosの処理を子reducerとして分割すると分かりやすくなります。
初期値の値の処理はそれぞれの子reducerに記述します。
これはreducer compositionと呼ばれていて、Reduxアプリケーションの基本的な構成です。

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)
  }
}

最後に、ReduxではcombineReducers()というユーティリティを提供しており、todoAppを書き換えることができます。combineReducerでは分割された子reducer名と同じキーのstateが使用されます。

import { combineReducers } from 'redux'

const todoApp = combineReducers({
  visibilityFilter,
  todos
})

export default todoApp

続き Redux入門 4日目 Reduxの基本・Stores

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
ユーザーは見つかりませんでした