Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating state managed by another reducer

Tags:

In my React app, my appReducer manages global stuff such as notifications, user info, etc.

One of the modules in the app is the inventory module which has its own reducer i.e. inventoryReducer. And in the redux store, I combine all the reducers.

When a user makes an inventory entry, in addition to handling the inventory transaction, I want to display an on-screen notification which is handled in the appReducer. How do I update the state of displayNotification which is under appReducer from the inventoryReducer?

The following is my app reducer:

import 'babel-polyfill'; import * as types from '../actions/actionTypes';  const initialState = {     displayNotification: {} };  export default (state = initialState, action) => {          switch (action.type) {          case types.DISPLAY_NOTIFICATION :              return Object.assign({}, state, {                 displayNotification: action.value             })          default: return state     } } 

And this is the inventoryReducer:

import 'babel-polyfill'; import * as types from '../actions/actionTypes';  const initialState = {     inventory: [] };  export default (state = initialState, action) => {          switch (action.type) {          case types.SET_INVENTORY :              return Object.assign({}, state, {                 inventory: action.inventoryItem             })          case types.DISPLAY_NOTIFICATION :              return Object.assign({}, state, {                 app.displayNotification: action.value // <-- Is this how I access `displayNotification` which is managed by the `appReducer`?                 })                  default: return state         }     } 

My update inventory action needs to dispatch both SET_INVENTORY and DISPLAY_NOTIFICATION. I'm trying to understand how I can update displayNotification from inventoryReducer where displayNotification is actually managed by the appReducer.

like image 458
Sam Avatar asked Nov 30 '16 23:11

Sam


2 Answers

Following up with what azium said:

I think what you're trying to do is the wrong approach. What's stopping you from a) listening to SET_INVENTORY in your appReducer or b) dispatch both actions from your component?

As far as I understand, in Redux each reducer is allocated a slice of the entire state object and their operations are restricted in that slice. They are not allowed to access the state slice managed by any other reducer, and they shouldn't do that.

The concept description of Redux is that it is a predictable state container. But when I look at what we are trying to achieve in this question, if we were to access/modify state managed by another reducer-B in our reducer-A, the predictability and maintainability of the app are compromised according to me.

Without compromising on anything or moving undesired logic into our components, we can achieve what we need.

Option 1

Inside appReducer

you create a type SET_INVENTORY which does what DISPLAY_NOTIFICATION does. You can have multiple subscriptions for the single action that dispatches type SET_INVENTORY (in appReducer and inventoryReducer).

As shown below, in appReducer, if the action type is either SET_INVENTORY or DISPLAY_NOTIFICATION, the reducer updates the key displayNotification.

export default (state = initialState, action) => {      switch (action.type) {          case types.SET_INVENTORY :          case types.DISPLAY_NOTIFICATION :              return Object.assign({}, state, {                 displayNotification: action.value             })          default: return state     } } 

Option 2

Create a method that couples the dispatching of two actions,

let's say you have an action

function setInventory(inventoryItem) {     return {         type: types.SET_INVENTORY,         inventoryItem     }; } 

and another action

function displayNotification(value) {     return {         type: types.DISPLAY_NOTIFICATION,         value,     }; } 

create a thunk to couple them:

export function notifyAndSetInventory(notify, inventoryItem) {     return dispatch => {         dispatch(displayNotification(notify));         return dispatch(setInventory(inventoryItem));     }; } 
like image 104
yadhu Avatar answered Sep 23 '22 10:09

yadhu


In Redux's official document there's a chapter called 'Beyond combineReducers'. It mentioned sharing data between slice reducers.

Sharing data between slice reducers

I personally prefer the third solution mentioned in the link, which is adding a third customized reducer to handle the "special" cases where data needs to be shared across slices, then use reduce-reducers to combine the new customized reducer and the original combined reducer (i.e. appReducer + inventoryReducer).

const crossSliceReducer = (state, action) => {   if (action.type === 'CROSS_SLICE_ACTION') {     // You can access both app and inventory states here   }   return state; } // Combine the reducers like you did before const combinedReducer({app: appReducer, inventory: inventoryReducer});  // Add the cross-slice reducer to the root reducer const rootReducer = reduceReducers(combinedReducer, crossSliceReducer) 
like image 22
Pineapple Express Avatar answered Sep 21 '22 10:09

Pineapple Express