Redux の考え方(基礎編)
そろそろ Redux 入門するかーと思って調べはじめたけど、結構難しかったので調べたことをまとめておきます。
たぶん、次回は Async のあたり読んで API コールとかどうすればいいの?
のあたりを調べる予定。
はじめに
Redux なんで使うの?とかはここでは記述しません。
本家や他 Web ページをご参照ください。
- http://redux.js.org/docs/introduction/Motivation.html
- https://medium.com/swlh/the-case-for-flux-379b7d1982c6
参考にしたドキュメントとか
- 公式ドキュメント
http://redux.js.org/docs/ - examples
https://github.com/rackt/redux/blob/master/examples
Redux の登場人物たち
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 読んだ章にチェックマークがついて、読み終わったとこがわかって便利。
なにげに捗ってる感が得られるのも気持ちいい。