ぶるーすくりーん

ぽんこつプログラマ日記

Redux の考え方(基礎編)

そろそろ Redux 入門するかーと思って調べはじめたけど、結構難しかったので調べたことをまとめておきます。

たぶん、次回は Async のあたり読んで API コールとかどうすればいいの?
のあたりを調べる予定。

はじめに

Redux なんで使うの?とかはここでは記述しません。
本家や他 Web ページをご参照ください。

参考にしたドキュメントとか

Redux の登場人物たち

f:id:tajima0111185:20160111185553p:plain

Actions

アプリケーションからstoreにデータを送信するための payload.

store.dispatch(action) を使用して送信する。

Reducers

アプリケーションの state を変更するための実装を書く場所。

変更前の state オブジェクトを受け取って、変更後の state を return する。

(previousState, action) => newState

reducer の中での決めゴト。

  • 引数で渡された state や action そのものを変更しちゃダメ
    state を変更するには、previousState を直接いじるのではなく、新しいオブジェクトを作成して返却する。
  • API 呼び出しや routing みたいな side effects を実行してはダメ
  • Calling non-pure functions, e.g. Date.now() or Math.random().

Given the same arguments, it should calculate the next state and return it. No surprises. No side effects. No API calls. No mutations. Just a calculation.

同じ引数が来たらいつでも同じ結果を返しましょう、もらった引数はいじらない !

Store

Redux アプリケーション内で1つだけ存在

アプリケーションの state を保持する場所。
state の取得や変更依頼は、Store 経由で行う。

  • state の取得 getState()
  • state の更新 dispatch(action)
  • リスナーの登録 subscribe(listener) これまだ理解してない

実装の仕方

Actions

react-redux を使用している場合は、connect() ヘルパーを使用して、 bindActionCreators() を使うと自動的にすべての Action Creator を自動的に dispatch() に bind できる。

action/todos.js

import * as types from '../constants/ActionTypes'

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

action/todos.js

import { bindActionCreators } from 'redux'
import * as TodoActions from '../actions/todos'

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(TodoActions, dispatch)
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App)

Reducers

reducers/todos.js

import { ADD_TODO } from '../constants/ActionTypes'

const initialState = []

function todoApp(state = initialState, action) {
  switch (action.type) {
    case ADD_TODO:
      return Object.assign({}, state, {
        todos: [
          ...state.todos,
          {
            text: action.text,
            completed: false
          }
        ]
      })

    default:
      return state
  }
}

引数で受け取った state はいじんないで、Object.assign() で新しいオブジェクト作成して return する。

この3点リーダみたいの、てっきり例だから省略してるのかと思ったら、ES6 記法だった。
babel stage es2015: Default + Rest + Spread

通常のプロジェクトだと、reducer は複数できるので、combineReducers() を使用してまとめるといい。

reducers/index.js

import { combineReducers } from 'redux'
import todos from './todos'

const rootReducer = combineReducers({
  todos
})

export default rootReducer

Store

store/configStore.js

import { createStore } from 'redux'
import rootReducer from '../reducers'

export default function configureStore(initialState) {
  const store = createStore(rootReducer, initialState)
  return store
}

React への組み込み

React に組み込む前に、Actions, Reducers, Store はそもそも UI から独立してるので、それだけでテストが書けるのでどんどんテストを書くべき。

index.js

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import App from './containers/App'
import configureStore from './store/configureStore'

const store = configureStore()

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

おまけ

redux の Documents 読んだ章にチェックマークf:id:tajima0111185:20160111173855p:plainがついて、読み終わったとこがわかって便利。
なにげに捗ってる感が得られるのも気持ちいい。