Redux - Combined Reducers

My reading notes for Code Fellows


Redux - Combined Reducers

Multiple Reducers Example

Why create multiple reducers? objects can have a lot of different types of information; name: string age: number behaviors [].

How would you combine multiple reducers? Using combineReducers will create multiple reducers that can be broken up into their own files.

import { combineReducers, createStore } from redux const userReducer = (state, actions) => { switch(action, type){ state.name = action.payload; break; } return state; }: const behaviorReducer = (state, actions) => { state.behavior = action.payload; return state; }: const reducers = combineReducers({ user: userReducer behavior: behaviorReducer })

How will you manage state as an immutable object? why?

Redux Docs: Using Combined Reducers

combineReducers is a utility function to simplify the most common use case when writing ___ _____ . Redux reducers

Explain how combineReducers assembles the new state tree. In order to assemble the new state tree, combineReducers will call each slice reducer with its current slice of state and the current action, giving the slice reducer a chance to respond and update its slice of state if needed. So, in that sense, using combineReducers does “call all reducers”, or at least all of the slice reducers it is wrapping.

How would you define initial state in an app using combineReducers? There are two main ways to initialize state for your application. The createStore method can accept an optional preloadedState value as its second argument. Reducers can also specify an initial value by looking for an incoming state argument that is undefined, and returning the value they’d like to use as a default. This can either be done with an explicit check inside the reducer, or by using the ES6 default argument value syntax: function myReducer(state = someDefaultValue, action).

It’s not always immediately clear how these two approaches interact. Fortunately, the process does follow some predictable rules. Here’s how the pieces fit together.

Without combineReducers() or similar manual code, preloadedState always wins over state = … in the reducer because the state passed to the reducer is preloadedState and is not undefined, so the ES6 argument syntax doesn’t apply.

With combineReducers() the behavior is more nuanced. Those reducers whose state is specified in preloadedState will receive that state. Other reducers will receive undefined and because of that will fall back to the state = … default argument they specify.

In general, preloadedState wins over the state specified by the reducer. This lets reducers specify initial data that makes sense to them as default arguments, but also allows loading existing data (fully or partially) when you’re hydrating the store from some persistent storage or the server.

Note: Reducers whose initial state is populated using preloadedState will still need to provide a default value to handle when passed a state of undefined. All reducers are passed undefined on initialization, so they should be written such that when given undefined, some value should be returned. This can be any non-undefined value; there’s no need to duplicate the section of preloadedState here as the default.

Redux Docs: Combined Reducer Syntax

Why will you want to split your reducing functions as your app becomes more complex? As your app grows more complex, you’ll want to split your reducing function into separate functions, each managing independent parts of the state.

The combineReducers helper function turns an object whose values are different reducing functions into a single reducing function you can pass to createStore.

The resulting reducer calls every child reducer, and gathers their results into a single state object. The state produced by combineReducers() namespaces the states of each reducer under their keys as passed to 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? ...
  }
}

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

The combineReducers() helper function turns an object whose values are different reducing functions into a single reducing function you can pass to createStore().

What is a popular convention when naming reducers?

A popular convention is to name reducers after the state slices they manage, so you can use ES6 property shorthand notation: combineReducers({ counter, todos }). This is equivalent to writing combineReducers({ counter: counter, todos: todos }).