メインコンテンツへスキップ

Redux 基本:Part 4 ストア

非公式ベータ版翻訳

このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →

学習内容
  • Redux ストアの作成方法
  • ストアを使用した状態更新と更新監視方法
  • ストアの拡張機能設定方法
  • Redux DevTools Extension のセットアップによるアプリのデバッグ方法

はじめに

Part 3: 状態、アクション、リデューサーでは、Todo アプリの実装を開始しました。ビジネス要件をリストアップし、アプリ動作に必要な状態構造を定義し、ユーザー操作で発生するイベントを表現するアクションタイプを作成しました。また、state.todosstate.filters セクションを更新するリデューサー関数を作成し、Redux の combineReducers 関数を使って各機能の「スライスリデューサー」から「ルートリデューサー」を作成する方法を確認しました。

Redux アプリの中核となるストアを使って、これらのパーツを統合する時が来ました。

非公式ベータ版翻訳

このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →

注意

このチュートリアルでは、Reduxの基本原則と概念を説明するために、あえて旧来のスタイルのReduxロジックパターンを使用しています。これらは、現代的なReduxアプリ開発の正しいアプローチとして推奨しているRedux Toolkitを使った「モダンRedux」パターンに比べてコード量が多くなります。このチュートリアルは_プロダクション環境で使用することを想定したものではありません_。

「モダンRedux」をRedux Toolkitで実践する方法については、以下のページを参照してください:

Redux ストア

Redux ストアは、アプリを構成する状態(state)、アクション(actions)、リデューサー(reducers)を統合します。ストアには次の責務があります:

重要な点として、Redux アプリケーションでは単一のストアのみを使用します。データ処理ロジックを分割したい場合、別々のストアを作成する代わりに、リデューサーの合成を使用し、結合可能な複数のリデューサーを作成します。

ストアの作成

すべての Redux ストアは単一のルートリデューサー関数を持ちます。前セクションでcombineReducers を使ったルートリデューサー関数を作成しました。このルートリデューサーはサンプルアプリの src/reducer.js で定義されています。このルートリデューサーをインポートし、最初のストアを作成しましょう。

Redux コアライブラリにはストアを作成する createStore API があります。store.js という新規ファイルを追加し、createStore とルートリデューサーをインポートします。次に createStore を呼び出し、ルートリデューサーを渡します:

src/store.js
import { createStore } from 'redux'
import rootReducer from './reducer'

const store = createStore(rootReducer)

export default store

初期状態の読み込み

createStore は第2引数として preloadedState 値を受け取れます。これを使用して、ストア作成時に初期データを追加できます。例えば:

  • サーバーから送信された HTML ページに含まれる値
  • localStorage に永続化され、ユーザーが再度ページを訪れた際に読み戻される値
storeStatePersistenceExample.js
import { createStore } from 'redux'
import rootReducer from './reducer'

let preloadedState
const persistedTodosString = localStorage.getItem('todos')

if (persistedTodosString) {
preloadedState = {
todos: JSON.parse(persistedTodosString)
}
}

const store = createStore(rootReducer, preloadedState)

アクションのディスパッチ

ストアが作成できたので、プログラムが動作するか検証しましょう!UI がなくても、更新ロジックをテストできます。

ヒント

このコードを実行する前に、src/features/todos/todosSlice.js に戻り、initialState からサンプルの todo オブジェクトをすべて削除して空の配列にしてください。これにより、この例の出力が読みやすくなります。

src/index.js
// Omit existing React imports

import store from './store'

// Log the initial state
console.log('Initial state: ', store.getState())
// {todos: [....], filters: {status, colors}}

// Every time the state changes, log it
// Note that subscribe() returns a function for unregistering the listener
const unsubscribe = store.subscribe(() =>
console.log('State after dispatch: ', store.getState())
)

// Now, dispatch some actions

store.dispatch({ type: 'todos/todoAdded', payload: 'Learn about actions' })
store.dispatch({ type: 'todos/todoAdded', payload: 'Learn about reducers' })
store.dispatch({ type: 'todos/todoAdded', payload: 'Learn about stores' })

store.dispatch({ type: 'todos/todoToggled', payload: 0 })
store.dispatch({ type: 'todos/todoToggled', payload: 1 })

store.dispatch({ type: 'filters/statusFilterChanged', payload: 'Active' })

store.dispatch({
type: 'filters/colorFilterChanged',
payload: { color: 'red', changeType: 'added' }
})

// Stop listening to state updates
unsubscribe()

// Dispatch one more action to see what happens

store.dispatch({ type: 'todos/todoAdded', payload: 'Try creating a store' })

// Omit existing React rendering logic

覚えておいてください:store.dispatch(action) を呼び出すたびに:

  • ストアは rootReducer(state, action) を呼び出す

    • このルートリデューサーは内部で他のスライスリデューサー(例: todosReducer(state.todos, action))を呼び出す可能性がある
  • ストアは内部に 新しい 状態値を保存する

  • ストアはすべてのリスナーサブスクリプションコールバックを呼び出す

  • リスナーが store にアクセスできる場合、store.getState() を呼び出して最新の状態値を読み取れるようになる

この例のコンソールログ出力を見ると、各アクションがディスパッチされるたびに Reduxの状態がどのように変化するかがわかります:

アクションディスパッチ後のRedux状態ログ

最後のアクションではアプリが何もログ出力していないことに注意してください。 これは unsubscribe() を呼び出してリスナーコールバックを削除したため、 アクションがディスパッチされた後に何も実行されなかったためです。

UIの作成を始める前に、アプリの動作を指定しました。 これにより、アプリが意図通りに動作するという確信が得られます。

情報

必要に応じて、リデューサーのテストを書いてみてください。純粋関数であるため、テストは簡単です。サンプルの stateaction を渡して結果を取得し、期待通りかどうかを確認します:

todosSlice.spec.js
import todosReducer from './todosSlice'

test('Toggles a todo based on id', () => {
const initialState = [{ id: 0, text: 'Test text', completed: false }]

const action = { type: 'todos/todoToggled', payload: 0 }
const result = todosReducer(initialState, action)
expect(result[0].completed).toBe(true)
})

Reduxストアの内部構造

Reduxストアの内部を覗いて動作を理解すると役立つ場合があります。 以下は約25行のコードで実装された動作するReduxストアのミニチュア版です:

miniReduxStoreExample.js
function createStore(reducer, preloadedState) {
let state = preloadedState
const listeners = []

function getState() {
return state
}

function subscribe(listener) {
listeners.push(listener)
return function unsubscribe() {
const index = listeners.indexOf(listener)
listeners.splice(index, 1)
}
}

function dispatch(action) {
state = reducer(state, action)
listeners.forEach(listener => listener())
}

dispatch({ type: '@@redux/INIT' })

return { dispatch, subscribe, getState }
}

この簡易版Reduxストアは十分に機能するため、これまでアプリで使用していた実際のRedux createStore 関数の代わりに使用できます(実際に試してみてください!)。 実際のReduxストアの実装はより長く複雑ですが、その大部分はコメント、警告メッセージ、エッジケースの処理です。

ご覧の通り、実際のロジックはかなり簡潔です:

  • ストアは現在の state 値と reducer 関数を内部に保持する

  • getState は現在の状態値を返す

  • subscribe はリスナーコールバックの配列を保持し、新しいコールバックを削除する関数を返す

  • dispatch はリデューサーを呼び出し、状態を保存し、リスナーを実行する

  • ストアは起動時に1つのアクションをディスパッチし、リデューサーを状態で初期化する

  • ストアAPIは {dispatch, subscribe, getState} を含むオブジェクトである

特に重要な点を強調します:getState は単に現在の state 値を返すだけです。 つまりデフォルトでは、現在の状態値を誤って変更することを防ぐものは何もありません! 以下のコードはエラーなく実行されますが、これは誤った操作です:

const state = store.getState()
// ❌ Don't do this - it mutates the current state!
state.filters.status = 'Active'

言い換えると:

  • getState() を呼び出したとき、Reduxストアは state 値の追加コピーを作成しません。 ルートリデューサー関数から返された参照と全く同じものです

  • Reduxストアは偶発的な変更を防ぐための追加処理を行いません。リデューサー内でもストア外でも状態を変更することは可能であり、常に変更を避けるように注意する必要があります。

意図せずにデータが変更されてしまう一般的な原因として、配列のソートがあります。array.sort()を呼び出すと実際に既存の配列が変更されます。もしconst sortedTodos = state.todos.sort()のように呼び出すと、意図せずに実際のストア状態を変更してしまうことになります。

ヒント

パート8: モダンなReduxでは、Redux Toolkitがどのようにしてリデューサ内での変更を防ぎ、リデューサ外での意図しない変更を検出して警告するかを見ていきます。

ストアの設定

これまでにcreateStorerootReducerpreloadedState引数を渡せることを見てきました。しかしcreateStoreはさらに1つの引数を取ることができ、これはストアの機能をカスタマイズし新たな能力を追加するために使用されます。

Reduxストアはストアエンハンサーと呼ばれるものでカスタマイズします。ストアエンハンサーは、元のReduxストアをラップする追加レイヤーを加えた特別なバージョンのcreateStoreのようなものです。拡張されたストアは、ストアのdispatchgetStatesubscribeといった関数を独自バージョンで提供することで、ストアの動作を変更できます。

このチュートリアルでは、ストアエンハンサーが実際にどのように動作するかの詳細には立ち入らず、使用方法に焦点を当てます。

エンハンサーを使ったストアの作成

プロジェクトにはsrc/exampleAddons/enhancers.jsファイルに2つのサンプルストアエンハンサーが用意されています:

  • sayHiOnDispatch: アクションがディスパッチされるたびにコンソールに'Hi'!とログ出力するエンハンサー

  • includeMeaningOfLife: getState()から返される値に常にmeaningOfLife: 42フィールドを追加するエンハンサー

まずsayHiOnDispatchを使ってみましょう。これをインポートしてcreateStoreに渡します:

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

ここではpreloadedState値がないため、代わりに第2引数としてundefinedを渡します。

次にアクションをディスパッチしてみましょう:

src/index.js
import store from './store'

console.log('Dispatching action')
store.dispatch({ type: 'todos/todoAdded', payload: 'Learn about actions' })
console.log('Dispatch complete')

コンソールを見てください。他の2つのログ文の間に'Hi!'が出力されているはずです:

sayHi store enhancer logging

sayHiOnDispatchエンハンサーは、元のstore.dispatch関数を独自のdispatchバージョンでラップしています。store.dispatch()を呼び出したとき、実際にはsayHiOnDispatchのラッパー関数を呼び出しており、これは元の関数を呼び出した後に'Hi'と表示します。

では次に、2つ目のエンハンサーを追加してみます。同じファイルからincludeMeaningOfLifeをインポートできますが、問題があります。createStoreは第3引数として1つのエンハンサーしか受け付けないのです! どうすれば2つのエンハンサーを同時に渡せるでしょうか?

本当に必要なのは、sayHiOnDispatchエンハンサーとincludeMeaningOfLifeエンハンサーの両方を単一の結合されたエンハンサーにマージする方法で、それを渡すことです。

幸いなことに、Reduxコアにはcompose関数が含まれており、複数のエンハンサーをマージするために使用できます。これを使ってみましょう:

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

ではこのストアを使用するとどうなるか見てみましょう:

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}

ログ出力結果は次のようになります:

meaningOfLife store enhancer logging

これで、両方のエンハンサーが同時にストアの動作を変更していることがわかります。sayHiOnDispatchdispatchの動作を変更し、includeMeaningOfLifegetStateの動作を変更しています。

ストアエンハンサーはストアを変更する非常に強力な方法であり、ほぼすべてのReduxアプリはストア設定時に少なくとも1つのエンハンサーを含めることになります。

ヒント

渡すpreloadedStateがない場合は、代わりにenhancerを第2引数として渡すことができます:

const store = createStore(rootReducer, storeEnhancer)

ミドルウェア

エンハンサーが強力なのは、ストアのメソッド(dispatchgetStatesubscribe)をすべてオーバーライドまたは置換できるためです。

しかし多くの場合、dispatchの動作をカスタマイズするだけで十分です。dispatch実行時にカスタム動作を追加できる方法があると便利でしょう。

Reduxはミドルウェアと呼ばれる特殊なアドオンを使用して、dispatch関数をカスタマイズできるようにしています。

ExpressやKoaのようなライブラリを使ったことがあれば、ミドルウェアを追加して動作をカスタマイズする概念に馴染みがあるでしょう。これらのフレームワークでは、ミドルウェアはフレームワークがリクエストを受信してからレスポンスを生成するまでの間に配置できるコードです。例えばExpressやKoaミドルウェアはCORSヘッダーの追加、ロギング、圧縮などを提供します。ミドルウェアの最大の利点はチェーンで合成可能なことで、単一プロジェクトで複数の独立したサードパーティ製ミドルウェアを使用できます。

ReduxミドルウェアはExpress/Koaミドルウェアとは異なる問題を解決しますが、概念的に似たアプローチを取ります。Reduxミドルウェアは、アクションのディスパッチからリデューサー到達までの間を拡張するサードパーティ製ポイントを提供します。 開発者はReduxミドルウェアをロギング、クラッシュレポート、非同期API通信、ルーティングなどに利用します。

Redux ミドルウェアは Express や Koa のミドルウェアとは異なる問題を解決しますが、概念的に類似したアプローチを取ります。Redux ミドルウェアは、アクションのディスパッチからリデューサーへの到達までの間に、サードパーティの拡張ポイントを提供します。 ロギング、クラッシュレポート、非同期 API との通信、ルーティングなど、様々な目的で Redux ミドルウェアが利用されます。

まずストアへのミドルウェア追加方法を説明し、その後独自ミドルウェアの作成方法を示します。

ミドルウェアの使用

ストアエンハンサーでReduxストアをカスタマイズできることは既に見てきました。Reduxミドルウェアは実際、Reduxに組み込まれた特別なストアエンハンサーである**applyMiddleware**上に実装されています。

既にエンハンサーの追加方法を知っているので、今すぐ実装できます。まずapplyMiddleware単体から始め、このプロジェクトに含まれる3つのサンプルミドルウェアを追加します。

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

名前が示す通り、各ミドルウェアはアクションがディスパッチされると数字を出力します。

ではディスパッチすると何が起こるでしょうか?

src/index.js
import store from './store'

store.dispatch({ type: 'todos/todoAdded', payload: 'Learn about actions' })
// log: '1'
// log: '2'
// log: '3'

コンソールに次の出力が表示されます:

ミドルウェアのロギング出力

この仕組みはどのようになっているのでしょうか?

ミドルウェアはストアのdispatchメソッド周囲にパイプラインを形成しますstore.dispatch(action)を呼び出すと、実際にはパイプラインの最初のミドルウェアを呼び出しています。ミドルウェアはアクションを検出すると任意の処理を実行できます。通常、リデューサーと同様に、ミドルウェアは関心のある特定のアクションタイプかどうかを確認します。該当するタイプならカスタムロジックを実行し、そうでなければ次のミドルウェアへアクションを渡します。

リデューサーと異なり、ミドルウェアは内部で副作用(タイムアウトや非同期ロジックなど)を持てます

この場合、アクションは次のように渡されます:

  1. print1ミドルウェア(store.dispatchとして見える部分)

  2. print2ミドルウェア

  3. print3ミドルウェア

  4. 本来のstore.dispatch

  5. store内部のルートリデューサー

これらはすべて関数呼び出しなので、呼び出しスタックから戻ります。そのためprint1ミドルウェアが最初に実行され、最後に終了します。

カスタムミドルウェアの作成

独自のミドルウェアを作成することも可能です。常に必要というわけではありませんが、カスタムミドルウェアはReduxアプリケーションに特定の動作を追加する優れた方法です。

Reduxミドルウェアは3つのネストされた関数の連なりとして記述されます。このパターンを見てみましょう。まずはfunctionキーワードを使って記述し、処理内容を明確にします:

// Middleware written as ES5 functions

// Outer function:
function exampleMiddleware(storeAPI) {
return function wrapDispatch(next) {
return function handleAction(action) {
// Do anything here: pass the action onwards with next(action),
// or restart the pipeline with storeAPI.dispatch(action)
// Can also use storeAPI.getState() here

return next(action)
}
}
}

これらの3つの関数の役割と引数について詳しく見ていきましょう。

  • exampleMiddleware: 外側の関数がミドルウェア本体です。applyMiddlewareによって呼び出され、ストアの{dispatch, getState}関数を含むstoreAPIオブジェクトを受け取ります。これらはストアの実際のdispatchおよびgetState関数と同じものです。このdispatch関数を呼び出すと、アクションはミドルウェアパイプラインの_先頭_に送られます(外側の関数は一度だけ呼び出されます)

  • wrapDispatch: 中間の関数はnextを引数として受け取ります。これはパイプライン内の 次のミドルウェア(このミドルウェアがシーケンスの最後の場合、nextは代わりに元のstore.dispatch関数)を指します。next(action)を呼び出すと、アクションが 次の ミドルウェアに渡されます(一度だけ呼び出されます)

  • handleAction: 内側の関数はactionを受け取り、アクションがディスパッチされるたびに呼び出されます

ヒント

関数名は任意に設定できますが、次の命名パターンが理解の助けになります:

  • 外側: someCustomMiddleware(ミドルウェア名)
  • 中間: wrapDispatch
  • 内側: handleAction

通常の関数であるため、ES2015のアロー関数でも記述可能です。return文が不要になるため簡潔になりますが、アロー関数や暗黙の戻り値に慣れていない場合は可読性が低下する可能性があります。

以下はアロー関数を使用した同じ例です:

const anotherExampleMiddleware = storeAPI => next => action => {
// Do something in here, when each action is dispatched

return next(action)
}

依然として3つの関数をネストして返していますが、暗黙の戻り値によってより簡潔に記述できます。

最初のカスタムミドルウェア

アプリケーションにロギング機能を追加する場合を考えましょう。ディスパッチされるアクションの内容と、レデューサー処理後の状態をコンソールで確認したいとします。

情報

以下のサンプルはTodoアプリ固有のものではありませんが、プロジェクトに追加して動作を確認できます。

このようなロギングミドルウェアを作成できます:

const loggerMiddleware = storeAPI => next => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', storeAPI.getState())
return result
}

アクションがディスパッチされる際の処理フロー:

  • handleAction関数の前半が実行され、'dispatching'を出力

  • アクションをnextに渡す(次のミドルウェアまたは実際のstore.dispatch

  • レデューサーが実行されて状態が更新され、next関数が制御を返す

  • storeAPI.getState()で更新後の状態を取得可能

  • nextミドルウェアから返されたresultを最終的に返却

ミドルウェアは任意の値を返せます。パイプライン先頭のミドルウェアの戻り値がstore.dispatch()の戻り値となります。例:

const alwaysReturnHelloMiddleware = storeAPI => next => action => {
const originalResult = next(action)
// Ignore the original result, return something else
return 'Hello!'
}

const middlewareEnhancer = applyMiddleware(alwaysReturnHelloMiddleware)
const store = createStore(rootReducer, middlewareEnhancer)

const dispatchResult = store.dispatch({ type: 'some/action' })
console.log(dispatchResult)
// log: 'Hello!'

別の例を見てみましょう。ミドルウェアは特定のアクションを検知して非同期処理を実行できます。特定のアクションを捕捉して遅延出力するミドルウェア:

const delayedMessageMiddleware = storeAPI => next => action => {
if (action.type === 'todos/todoAdded') {
setTimeout(() => {
console.log('Added a new todo: ', action.payload)
}, 1000)
}

return next(action)
}

このミドルウェアは「todo added」アクションを検出し、1秒の遅延後にアクションのペイロードをコンソールへ出力します。

ミドルウェアの使用例

では、ミドルウェアで何ができるでしょうか?可能性は無限大です!

ミドルウェアはディスパッチされたアクションを検知すると、自由に処理を実行できます:

  • コンソールへのログ出力

  • タイムアウトの設定

  • 非同期API呼び出しの実行

  • アクションの内容変更

  • アクションの一時停止や完全停止

その他、考えられるあらゆる処理が可能です。

特に、ミドルウェアは副作用を伴うロジックを包含するために設計されています。さらに、ミドルウェアはdispatchを拡張し、通常のアクションオブジェクト以外も受け入れられるようにできます。これらの詳細についてはパート6: 非同期ロジックで解説します。

Redux DevTools

最後に、ストア設定に関する非常に重要なトピックを説明します。

Reduxは状態がいつ、どこで、なぜ、どのように変化したかを理解しやすくするために特別に設計されました。この設計思想に基づき、Redux DevToolsの使用が可能になっています。この拡張機能は、ディスパッチされたアクションの履歴、アクションの内容、各アクション後の状態変化を可視化します。

Redux DevTools UIはChromeFirefox向けのブラウザ拡張として提供されています。まだインストールしていない場合は、今すぐ追加してください。

インストール後、ブラウザのDevToolsウィンドウを開くと、新しい「Redux」タブが表示されます。ただし現時点では機能しません。まずReduxストアとの連携設定が必要です。

ストアへのDevTools追加

拡張機能のインストール後、DevToolsが内部処理を監視できるようストアを設定する必要があります。これには特定のストアエンハンサーが必要です。

Redux DevTools Extensionドキュメントに設定方法が記載されていますが、手順が複雑です。代わりにredux-devtools-extensionパッケージを使用すると、複雑な設定を簡略化できます。このパッケージは、標準のRedux compose関数の代わりに使用できるcomposeWithDevTools関数を提供します。

実装例は次の通りです:

src/store.js
import { createStore, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import rootReducer from './reducer'
import { print1, print2, print3 } from './exampleAddons/middleware'

const composedEnhancer = composeWithDevTools(
// EXAMPLE: Add whatever middleware you actually want to use here
applyMiddleware(print1, print2, print3)
// other store enhancers if any
)

const store = createStore(rootReducer, composedEnhancer)
export default store

index.js がストアをインポートした後もアクションをディスパッチしていることを確認してください。次に、ブラウザの DevTools ウィンドウで Redux DevTools タブを開きます。次のような表示が確認できるはずです:

Redux DevTools拡張機能: アクションタブ

左側にはディスパッチされたアクションのリストがあります。いずれかをクリックすると、右側のパネルに複数のタブが表示されます:

左側にディスパッチされたアクションのリストが表示されます。いずれかをクリックすると、右パネルに次のタブが表示されます:

  • 該当アクションオブジェクトの内容

  • リデューサ処理後のRedux状態全体

  • 有効にした場合、最初に store.dispatch() を呼び出したコード行まで遡る関数スタックトレース

その「add todo」アクションをディスパッチした後の「State」タブと「Diff」タブの外観は次のとおりです:

Redux DevTools 拡張機能: state タブ

Redux DevTools Extension: 差分タブ

これらは非常に強力なツールで、アプリケーションのデバッグや内部動作の正確な理解に役立ちます。

学んだこと

これまで見てきたように、ストアはすべての Redux アプリケーションの中核となる要素です。ストアは状態(state)を保持し、レデューサーを実行してアクションを処理します。また、追加の動作を加えるためにカスタマイズすることも可能です。

現在のサンプルアプリの状態を見てみましょう:

復習として、このセクションでカバーした内容を以下にまとめます:

まとめ
  • Redux アプリケーションは常に単一のストアを持つ
    • ストアは Redux の createStore API で作成される
    • すべてのストアは単一のルートレデューサー関数を持つ
  • ストアには主に3つのメソッドがある
    • getState は現在の状態を返す
    • dispatch はアクションをレデューサーに送信して状態を更新する
    • subscribe はアクションがディスパッチされるたびに実行されるリスナーコールバックを登録する
  • ストアエンハンサーで作成時のカスタマイズが可能
    • エンハンサーはストアをラップし、メソッドをオーバーライドできる
    • createStore は1つのエンハンサーを引数として受け入れる
    • 複数のエンハンサーは compose API で結合できる
  • ミドルウェアがストアカスタマイズの主要な方法
    • ミドルウェアは applyMiddleware エンハンサーで追加される
    • ミドルウェアは3層のネストされた関数で構成される
    • アクションがディスパッチされるたびにミドルウェアが実行される
    • ミドルウェア内部で副作用を扱える
  • Redux DevTools でアプリケーションの経時変化を確認可能
    • DevTools 拡張機能をブラウザにインストールできる
    • ストアに composeWithDevTools で DevTools エンハンサーを追加する必要がある
    • DevTools はディスパッチされたアクションと状態の経時変化を表示する

次のステップ

これで、レデューサーを実行し、アクションをディスパッチした際に状態を更新できる、機能する Redux ストアが完成しました。

ただし、すべてのアプリケーションにはデータを表示し、ユーザーが実用的な操作を行える UI が必要です。Part 5: UI and Reactでは、Redux ストアが UI とどのように連携するか、特に Redux が React とどのように統合するかを見ていきます。