Правильное использование понижающих редукторов

Я не понимаю, для чего предназначены редукторы. Следует ли использовать его в том случае, если у меня есть 2 функции редуктора, содержащие одно и то же действие?

function reducerA(state, action){
   switch(action.type):
       ...
       case 'SAME_ACTION': {...state, field: state.field+1}
}

function reducerB(state, action){
   switch(action.type):
       ...
       case 'SAME_ACTION': {...state, field: state.field*2}
}

Поэтому, если я вызову reduceReducer для reducerA и reducerB и для {field: 0} будет вызвано действие "SAME_ACTION", тогда у меня будет следующее состояние {field: 2}?

Также мне кажется, что это своего рода конкатенация редукторов (то есть объединение их под одним ключом).

Я прав или reduceReducer служит другой цели?

Ответ 1

Разница:

  • combineReducers создает вложенное состояние state
  • reduceReducers создает состояние state

Рассмотрим следующие редукторы. Не существует типов действий, которые бы упростили ситуацию:

// this reducer adds a payload to state.sum 
// and tracks total number of operations
function reducerAdd(state, payload) {
  if (!state) state = { sum: 0, totalOperations: 0 }
  if (!payload) return state

  return {
    ...state,
    sum: state.sum + payload,
    totalOperations: state.totalOperations + 1
  }
}

// this reducer multiplies state.product by payload
// and tracks total number of operations
function reducerMult(state, payload) {
  if (!state) state = { product: 1, totalOperations: 0 }
  if (!payload) return state

  // 'product' might be undefined because of 
  // small caveat in 'reduceReducers', see below
  const prev = state.product || 1

  return {
    ...state,
    product: prev * payload,
    totalOperations: state.totalOperations + 1
  }
}

combineReducers

Каждый редуктор получает независимую часть состояния (см. также http://redux.js.org/docs/api/combineReducers.html):

const rootReducer = combineReducers({
  add: reducerAdd,
  mult: reducerMult
})

const initialState = rootReducer(undefined)
/*
 * {
 *   add:  { sum: 0, totalOperations: 0 },
 *   mult: { product: 1, totalOperations: 0 },
 * }
 */


const first = rootReducer(initialState, 4)
/*
 * {
 *   add:  { sum: 4, totalOperations: 1 },
 *   mult: { product: 4, totalOperations: 1 },
 * }
 */    
// This isn't interesting, let look at second call...

const second = rootReducer(first, 4)
/*
 * {
 *   add:  { sum: 8, totalOperations: 2 },
 *   mult: { product: 16, totalOperations: 2 },
 * }
 */
// Now it obvious, that both reducers get their own 
// piece of state to work with

reduceReducers

Все редукторы находятся в одном состоянии

const addAndMult = reduceReducers(reducerAdd, reducerMult) 

const initial = addAndMult(undefined)
/* 
 * {
 *   sum: 0,
 *   totalOperations: 0
 * }
 *
 * First, reducerAdd is called, which gives us initial state { sum: 0 }
 * Second, reducerMult is called, which does not have payload, so it 
 * just returns state unchanged. 
 * That why there isn't any 'product' prop.
 */ 

const next = addAndMult(initial, 4)
/* 
 * {
 *   sum: 4,
 *   product: 4,
 *   totalOperations: 2
 * }
 *
 * First, reducerAdd is called, which changes 'sum' = 0 + 4 = 4
 * Second, reducerMult is called, which changes 'product' = 1 * 4 = 4
 * Both reducers modify 'totalOperations'
 */


const final = addAndMult(next, 4)
/* 
 * {
 *   sum: 8,
 *   product: 16,
 *   totalOperations: 4
 * }
 */

Примеры использования

  • combineReducers - каждый редуктор управляет собственным срезом состояния (например, state.todos и state.logging). Это полезно при создании корневого редуктора.
  • reduceReducers - каждый редуктор управляет одним и тем же состоянием. Это полезно при объединении нескольких редукторов, которые должны работать в одном и том же состоянии (это может случиться, например, при объединении нескольких редукторов, созданных с использованием handleAction из redux-actions)

Отличие очевидно от формы конечного состояния.

Предостережения

В reduceReducers есть небольшая оговорка: когда конечный редуктор вызывается с помощью state = undefined, он должен вернуть исходное состояние. Однако только первый редуктор в цепочке получает undefined, все остальные редукторы будут получать состояние от первого.

Ответ 2

Я думаю, что это как combineReducers из библиотеки redux, где вы можете комбинировать несколько редукторов с одним редуктором.

В вашем случае, если вы объедините эти 2 редуктора, вы получите один редуктор, который в конце будет представлен таким состоянием:

state = {
 reducerA: {
  ...,
  field: number
 },
 reducerB: {
  ...,
  field: number
 }
}

Он не предназначен для объединения одного и того же действия с разными редукторами, которые вы можете выполнить, просто напишите один и тот же случай для обоих, как и вы.

Ответ 3

Я также не понимаю, что пытаются решить редукторы. Вариант использования, описанный @Tomáš, может быть реализован с помощью простого редуктора. В конце концов, Reducer - это просто функция, которая принимает состояние приложения и действие и возвращает объект, содержащий новое состояние приложения. Например, вы можете сделать следующее, вместо того чтобы использовать предоставленный CombineReducers с помощью приставки:

import combinationReducer from "./combinationReducer";
import endOfPlayReducer from "./endOfPlayReducer";
import feedbackReducer from "./feedbackReducer";

function combineReducers(appState, action) {
  return {
    combination: combinationReducer(appState, action),
    feedbacks: feedbackReducer(appState, action),
    endOfPlay: endOfPlayReducer(appState, action)
  };
}

И, конечно, здесь ваши редукторы принимают все состояние приложения и возвращают только фрагмент, за который они отвечают. Опять же, это просто функция, вы можете настроить ее так, как вам нравится. Вы можете прочитать больше об этом здесь