granmoe
granmoe

Reputation: 322

How to handle cross-cutting concerns in redux reducers and actions

Given a use case like the one in this question:

Best way to update related state fields with split reducers?

What is the best practice for dealing with actions in reducers that depend on state outside of their own state? The author of the question above ended up just passing the entire state tree as a third argument to every reducer. This seems heavy-handed and risky. The Redux FAQ lists the following potential solutions:

  • If a reducer needs to know data from another slice of state, the state tree shape may need to be reorganized so that a single reducer is handling more of the data.
  • You may need to write some custom functions for handling some of these actions. This may require replacing combineReducers with your own top-level reducer function.
  • You can also use a utility such as reduce-reducers to run combineReducers to handle most actions, but also run a more specialized reducer for specific actions that cross state slices.
  • Async action creators such as redux-thunk have access to the entire state through getState(). An action creator can retrieve additional data from the state and put it in an action, so that each reducer has enough information to update its own state slice.

In my use case, I have an action "continue" that determines what page a user is allowed to go to in a multiple-form / multi-step process, and since this depends on pretty much the entire app state, I can't handle it in any of my child reducers. For now, I've pulled the store into the action creator. I use the current state of the store to calculate an action object that fires to my "page" reducer, which changes the active page. I will probably install redux-thunk and use getState() in this action creator, but I'm not committed to this approach yet.

I guess this isn't too bad of a solution since there is only one action (so far) that must be handled this way. I'm just wondering if there is a better solution, or if there is a way to re-structure my state and reducers to make it easier, or if what I'm doing is within best practices for Redux. If there are any similar examples out there, that would be helpful also.

To give some more context, my state tree currently looks like this:

{
  order: order.result,
  items: order.entities.items,
  activePage: {
    id: 'fulfillment'
    // page info
  },
  pagesById: { // all the possible pages
    fulfillment: {
      id: 'fulfillment'
      // page info
    }
  }
}

The active page is the page / section in which the user must enter data in order to proceed to the next page). Determining the active page almost always depends on the items state and sometimes depends on order state. The end result is an app where the user fills out a few forms in succession, hitting continue once the form is valid. On continue the app determines the next page needed and displays it, and so on.

EDIT: We've tried the approach of implementing a "global" reducer in combination with child reducers.

The implementation is like this...

const global = (currentState = initialState, action) => {
  switch (action.type) {
    default:
      return currentState
  }
}


const subReducers = combineReducers({
  order,
  meta
})

export default function (currentState = initialState, action) {
  var nextState = global(currentState, action)
  return subReducers(nextState, action)
}

The global reducer is first run on the whole app state, then the result of that is fed to the child reducers. I like the fact that I'm no longer putting a bunch of logic in action creators just to read different parts of state.

I believe this is in alignment with the principles of redux since every action still hits every reducer, and the order in which reducers are called is always the same. Any thoughts on this implementation?

EDIT: We are now using router libraries to handle the page state, so activePage and pagesById are gone.

Upvotes: 6

Views: 1221

Answers (1)

Exayy
Exayy

Reputation: 618

If state.activePage depends of state.order and state.items, you may subscribe to the store and in case of modifications on "order" or "items" then dispatch a "checkPage" action which can set another active page if necessary. One way should to connect on a "top component" order and items, listen their values and change active page/redirect

Not easy to understand your concern, I hope my message will help. Good luck

Upvotes: 0

Related Questions