161
116

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-11-22

前回 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

161
116
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
161
116

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?