本文へ移動

combineReducers(reducers)

概要

combineReducers ヘルパー関数は、値が異なる「スライスリデュース」関数であるオブジェクトを、Redux Toolkit の configureStore(または従来の createStore メソッド)に渡せる単一の複合リデュース関数に変換します。

生成された複合リデュースは、アクションがディスパッチされるたびにすべてのスライスリデュースを呼び出し、その結果を単一のステートオブジェクトにまとめます。これにより、リデュースロジックを個別の関数に分割し、それぞれがステートの独自のセクションを独立して管理できるようになります。

ヒント

これはめったに必要ありません - Redux Toolkit の configureStore メソッド は、スライスリデュースのオブジェクトを渡すと自動的に combineReducers を呼び出します。

const store = configureStore({
reducer: {
posts: postsReducer,
comments: commentsReducer
}
})

ルートリデュースを手動で最初に構築する必要がある場合は、combineReducers() を自分で呼び出すことができます。

ステートのスライス

combineReducers() によって生成されるステートは、combineReducers() に渡されたキーの下に各リデュースのステートを名前空間化します。

rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer})
// This would produce the following state object
{
potato: {
// ... potatoes, and other state managed by the potatoReducer ...
},
tomato: {
// ... tomatoes, and other state managed by the tomatoReducer, maybe some nice sauce? ...
}
}

渡されたオブジェクト内のリデュースに異なるキーを使用することで、ステートキー名を制御できます。たとえば、ステートの形状が { todos, counter } となるように、combineReducers({ todos: myTodosReducer, counter: myCounterReducer }) を呼び出すことができます。

引数

  1. reducers (オブジェクト): 1 つに結合する必要がある異なるリデュース関数に対応する値を持つオブジェクト。
combineReducers({
posts: postsReducer,
comments: commentsReducer
})

渡された各リデュースが従うべきルールについては、下記の注意事項を参照してください。

戻り値

(関数): reducers オブジェクト内の各リデュースを呼び出し、同じ形状のステートオブジェクトを構築するリデュース。

注意事項

この関数はわずかに主観的で、初心者がよくある落とし穴を回避するのに役立つように傾いています。そのため、ルートリデュースを手動で記述する場合に必ずしも従う必要がないいくつかのルールを強制しようとしています。

combineReducers に渡されるリデュースは、次のルールを満たす必要があります。

  • 認識されないアクションに対しては、最初の引数として渡された state を返す必要があります。

  • undefined を返すことは決してありません。早期の return 文によって誤ってこれを行うのは非常に簡単であるため、combineReducers は、エラーが他の場所で発生するのを許すのではなく、そうした場合に例外をスローします。

  • 渡された stateundefined の場合、この特定のリデュースの初期ステートを返す必要があります。前のルールによると、初期ステートも undefined であってはなりません。オプションの引数構文で指定するのが便利ですが、最初の引数が undefined であるかどうかを明示的に確認することもできます。

combineReducers は、リデュースがこれらのルールのいくつかを満たしているかどうかを確認しようとしますが、これらのルールを覚えておき、可能な限り従うようにする必要があります。combineReducers は、undefined を渡すことによってリデュースをチェックします。これは、Redux.createStore(combineReducers(...), initialState) に初期ステートを指定した場合でも実行されます。したがって、独自のコードで実際に undefined を受信することを意図していない場合でも、リデュースが undefined をステートとして受信したときに正しく動作することを**必ず**確認する必要があります。

reducers/todos.js

export default function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([action.text])
default:
return state
}
}

reducers/counter.js

export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}

reducers/index.js

import { combineReducers } from '@reduxjs/toolkit'
import todos from './todos'
import counter from './counter'

export default combineReducers({
todos,
counter
})

App.js

import { configureStore } from '@reduxjs/toolkit'
import reducer from './reducers/index'

const store = configureStore({
reducer
})
console.log(store.getState())
// {
// counter: 0,
// todos: []
// }

store.dispatch({
type: 'ADD_TODO',
text: 'Use Redux'
})
console.log(store.getState())
// {
// counter: 0,
// todos: [ 'Use Redux' ]
// }

ヒント

  • このヘルパーは単なる便宜上のものです!独自の combineReducers を記述して 異なる動作をするように することも、他の関数と同じように明示的にルートリデュース関数を記述し、子リデュースからステートオブジェクトを手動でアセンブルすることもできます。

  • リデュース階層の任意のレベルで combineReducers を呼び出すことができます。トップレベルで行う必要はありません。実際には、複雑になりすぎた子リデュースを独立した孫リデュースなどに分割するために、再度使用できます。