How to update two values in two different reducers at the same time in Redux / Atomic operations

2.5k views Asked by At

Project description: I've a calendar like Google Calendar, where you have zoomIn and zoomOut buttons, and also a datepicker where you can click any date to move your calendar to that date.

Design of store: I've a filters reducer where I keep the range of the current calendar (for example {start: '01/01/2016', end: '01/01/2017'}), and also a UI reducer where I keep the current zoom level (one of year/month/week)

Scenario: When someone is on zoomLevel='year' and clicks on a date on the datepicker, I need to go to zoomLevel='week' and also modify the range.

Problem: How to update the store with both zoomLevel and range, at the same time? If I don't, my calendar breaks due to inconsistency because when I update the first value the user interface renders everything.

UI reducer as an example of generic reducer:

import * as actions from './action-types'
import { omit } from 'lodash'
import initialState from './initial-state'
export * from './actions'
export * from './selectors'

// TODO: Eval. Tendria mas sentido hacer el switch por dominio?
export default function ui(state = initialState, {type, meta: {domain = 'general'} = {}, payload: {key, value} = {}}) {
  switch (type) {
  case actions.SET:
    return {
      ...state,
      [domain]: {
        ...state[domain] || {},
        [key]: value
      }
    }
  case actions.TOGGLE:
    return {
      ...state,
      [domain]: {
        ...state[domain] || {},
        [key]: !!!(state[domain] || {})[key]
      }
    }
  case actions.TOGGLE_IN_ARRAY:
    // No sirve para objetos
    const index = state[domain] && state[domain][key] ? state[domain][key].indexOf(value) : -1
    return index === -1 ?
      {
        ...state,
        [domain]: {
          ...state[domain] || {},
          [key]: [
            value,
            ...state[domain][key]
          ]
        }
      } : {
        ...state,
        [domain]: {
          ...state[domain],
          [key]: [
            ...state[domain][key].slice(0, index),
            ...state[domain][key].slice(index + 1)
          ]
        }
      }
  case actions.DELETE:
    return {
      ...state,
      [domain]: omit(state[domain], key)
    }
  case actions.CLEAR_DOMAIN:
    return {
      ...state,
      [domain]: initialState[domain]
    }
  case actions.RESET_UI:
    return {
      ...initialState
    }
  default:
    return state
  }
}
1

There are 1 answers

1
Yangshun Tay On

Dispatched actions will be sent to all reducers in the root reducer which will in turn be passed down to child reducers (depending on reducer implementations).

Hence if your root reducer looks like this:

export default combineReducers({
  filters,
  ui,
});

You can respond to the same action like this in each reducer:

function filters(state, action) {
  case CLICK_ON_DATE:
    return {
      ...state,
      start: ...,
      end: ...,
    };
  ...  
}


function ui(state, action) {
  case CLICK_ON_DATE:
    return {
      ...state,
      zoomLevel: 'week',
    };
  ...  
}