[前回] Django+Reactで学ぶプログラミング基礎(34): Reduxチュートリアル(TodoアプリのStore)
はじめに
前回は、TodoアプリのStoreを実装しました。
今回は、Storeのカスタマイズ方法を勉強します。
今回の内容
- Redux Store
- Store Enhancer
- Redux Middleware
Store Enhancer
Store Enhancerとは
-
Storeをカスタマイズし、機能拡張するためのもの
- Store作成時に、
createStore
の第3引数として渡される - 元の
dispatch/getState/subscribe
を書き換えることで、Storeを拡張
- Store作成時に、
-
Store Enhancerサンプル
-
src/exampleAddons/enhancers.js
-
sayHiOnDispatch
- actionがdispatchされる度に
'Hi'!
をコンソールにログ出力
- actionがdispatchされる度に
-
includeMeaningOfLife
-
getState()
の返却値にフィールドmeaningOfLife: 42
を追加
-
-
-
sayHiOnDispatch
Enhancerを使用しStoreを作成
- まず、
sayHiOnDispatch
をインポートし、createStore
の第3引数に渡す-
preloadedState
値がないため、undefined
を第2引数として渡す
-
src/store.js
import { createStore } from 'redux'
import rootReducer from './reducer'
import { sayHiOnDispatch } from './exampleAddons/enhancers'
const store = createStore(rootReducer, undefined, sayHiOnDispatch)
export default store
- 次に、actionをdispatch
-
sayHiOnDispatch
Enhancerは、元のstore.dispatch
関数を独自のdispatchでラップ-
store.dispatch()
呼び出しは、実際sayHiOnDispatch
からラッパー関数を呼び出す- 元の関数を呼び出してから
Hi!
を出力
- 元の関数を呼び出してから
-
-
src/index.js
import store from './store'
console.log('Dispatching action')
store.dispatch({ type: 'todos/todoAdded', payload: 'Learn about actions' })
console.log('Dispatch complete')
- VS Codeのターミナルで、アプリを起動すると、ブラウザが開かれる
C:\kanban\todo>cd redux-fundamentals-example-app
C:\kanban\todo\redux-fundamentals-example-app>npm start
includeMeaningOfLife
Enhancerを使用し、storeを作成
-
createStore
の第3引数は、1つのEnhancerのみ受け入れられる- 2つのEnhancerを同時に渡すには1つにマージする必要あり
-
Reduxコアの
compose
関数を使用し、複数Enhancerをマージ-
sayHiOnDispatch
とincludeMeaningOfLife
-
src/store.js
import { createStore, compose } from 'redux'
import rootReducer from './reducer'
import {
sayHiOnDispatch,
includeMeaningOfLife
} from './exampleAddons/enhancers'
const composedEnhancer = compose(sayHiOnDispatch, includeMeaningOfLife)
const store = createStore(rootReducer, undefined, composedEnhancer)
export default store
- 作成したstoreを使ってみる
- 二つのEnhancerが同時にstoreの動作を変更している
-
sayHiOnDispatch
はdispatch
の動作を変更-
'Hi'!
をログ出力
-
-
includeMeaningOfLife
はgetState
の動作を変更- フィールド
meaningOfLife: 42
を追加
- フィールド
-
- 二つのEnhancerが同時にstoreの動作を変更している
src/index.js
import store from './store'
store.dispatch({ type: 'todos/todoAdded', payload: 'Learn about actions' })
// log: 'Hi!'
console.log('State after dispatch: ', store.getState())
// log: {todos: [...], filters: {status, colors}, meaningOfLife: 42}
-
ブラウザで、デベロッパーツールのコンソールを確認
-
Store Enhancerのまとめ
- Storeを変更する強力な手段
-
dispatch、getState、subscribe
のいずれかをオーバーライドまたは置換できる
-
- Reduxアプリのstoreセットアップ時、少なくとも1つのEnhancerが含まれる
- Storeを変更する強力な手段
ミドルウェア(Middleware)
Middlewareとは
- リクエストを受信するフレームワークとレスポンスを生成するフレームワークの間に配置できるコード
- Middlewareの特徴は、チェーン構造(パイプライン)
- 1つのプロジェクトで複数の独立したサードパーティMiddlewareを使用できる
Redux Middlewareとは
- Reduxの拡張機能(Addon)で、actionのdispatchをカスタマイズする
- actionをdispatchしてからreducerに到達するまで、以下拡張を実施可能
- ロギング
- クラッシュレポート
- 非同期APIとの通信
- ルーティング
- ※ Reducerと異なり、Middlewareでは、タイムアウトやその他非同期ロジックによる副作用が発生する可能性あり
Redux Middlewareをstoreに追加
- Redux Middlewareは、Redux組み込みの
applyMiddleware
Store Enhancer上に実装されている - まず、
applyMiddleware
を使用し、3つのMiddlewareを追加- これらのMiddlewareは、actionがdispatchされると番号を出力する
src/store.js
import { createStore, applyMiddleware } from 'redux'
import rootReducer from './reducer'
import { print1, print2, print3 } from './exampleAddons/middleware'
const middlewareEnhancer = applyMiddleware(print1, print2, print3)
// Pass enhancer as the second arg, since there's no preloadedState
const store = createStore(rootReducer, middlewareEnhancer)
export default store
- 次に、actionをdispatch
src/index.js
import store from './store'
store.dispatch({ type: 'todos/todoAdded', payload: 'Learn about actions' })
// log: '1'
// log: '2'
// log: '3'
Middlewareのパイプライン
-
storeのdispatchに、下記Middlewareのパイプラインが形成される
-
store.dispatch(action)
を呼び出すと- パイプラインの最初のMiddlewareが呼び出される
- Middlewareは、actionのtypeが処理対象か確認
- 処理対象の場合、Middlewareはカスタムロジックを実行
- 処理対象でない場合、actionをパイプライン内の次のMiddlewareに渡す
- Middlewareは、actionのtypeが処理対象か確認
- パイプラインの最初のMiddlewareが呼び出される
-
-
Middlewareパイプラインで、actionが渡される順番
- 1.
print1
Middleware(store.dispatchと見なされる) - 2.
print2
Middleware - 3.
print3
Middleware - 4. 元のstore.dispatch
- 5. store内のroot reducer
- 1.
-
※ 上記はすべて関数呼び出しで、コールスタックからreturnされる
- よって、
print1
Middlewareが最初に実行され、最後に終了
- よって、
おわりに
ReduxのEnhancerとMiddlewareを使用し、Storeをカスタマイズしました。
次回も続きます。お楽しみに。