combineReducers | Redux (original) (raw)

combineReducers(reducers)

Overview

The combineReducers helper function turns an object whose values are different "slice reducer" functions into a single combined reducer function you can pass to Redux Toolkit's configureStore (or the legacy createStore method)

The resulting combined reducer calls every slice reducer any time an action is dispatched, and gathers their results into a single state object. This enables splitting up reducer logic into separate functions, each managing their own slice of the state independently.

tip

This should be rarely needed - Redux Toolkit's configureStore method will automatically call combineReducers for you if you pass in an object of slice reducers:

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

You can still call combineReducers() yourself if you need to construct the root reducer manually first.

State Slices

The state produced by combineReducers() namespaces the states of each reducer under their keys as passed to combineReducers()

Example:

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? ...
  }
}

You can control state key names by using different keys for the reducers in the passed object. For example, you may call combineReducers({ todos: myTodosReducer, counter: myCounterReducer }) for the state shape to be { todos, counter }.

Arguments

  1. reducers (Object): An object whose values correspond to different reducer functions that need to be combined into one.
combineReducers({
  posts: postsReducer,
  comments: commentsReducer
})

See the notes below for some rules every passed reducer must follow.

Returns

(Function): A reducer that invokes every reducer inside the reducers object, and constructs a state object with the same shape.

Notes

This function is mildly opinionated and is skewed towards helping beginners avoid common pitfalls. This is why it attempts to enforce some rules that you don't have to follow if you write the root reducer manually.

Any reducer passed to combineReducers must satisfy these rules:

While combineReducers attempts to check that your reducers conform to some of these rules, you should remember them, and do your best to follow them. combineReducers will check your reducers by passing undefined to them; this is done even if you specify initial state to Redux.createStore(combineReducers(...), initialState). Therefore, you must ensure your reducers work properly when receiving undefined as state, even if you never intend for them to actually receive undefined in your own code.

Example

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' ]
// }

Tips