My team and I are busy designing a redux store, including all the possible actions that can be fired. Due to the nature of our application, we have some inherent coupling/dependencies between different nodes (branches?) of our redux store. Our idea of tackling these dependencies in the most scalable manner was to extract it to its own separate node in the state tree. Let me give a simplified example to illustrate the structure we have in mind, and get to the problem that we're facing:
Say our state tree has the following:
You're probably thinking, why do it this way? Why not just integrate the dependency into the reducer for SectionB? The reason is that these dependencies vary per client, and we're reading them from a database. It can link any part of the state tree to any other part and have its own actions that need to happen.
Now, my question is, how do we reduce actions while taking into account these dependencies? If our application flow is:
What happens if we introduce a SectionC, with data C that is dependent on data B? It seems like a new action needs to be fired when the DependencySection updates data B, with the action being that data B has been updated. This would mean firing an action while another action is being reduced, which is obviously not allowed. Alternatively, it seems like making the different reducer sections execute in a VERY specific order would also solve the issue, but this is surely an anti-pattern.
The only solution we can come up with is to have middleware that repeatedly fires UPDATE actions after every action until the state no longer changes. For example, after the TOGGLE_DATA_A action updates data B (via the dependency on data A), the next UPDATE action would update data C (via the dependency on data B), and the next UPDATE would update nothing, stopping the loop. This is quite hacky.
So, is there a better way to deal with reducing such a dependent state? Or should we be structuring our state tree differently?
One of the key concepts behind Redux is that reducer logic is just functions, and if you need to order your state update handling in a specific order, you can do that yourself by writing explicit code for that. So no, "making different reducer sections execute in a specific order" is not an "anti-pattern", it's absolutely a valid and encouraged approach with Redux.
There's examples and discussions of this approach in the Structuring Reducers - Beyond combineReducers
section of the Redux docs, and in my blog posts Idiomatic Redux: The Tao of Redux, Part 1 - Implementation and Intent and Practical Redux, Part 7: Feature Reducers. I'll paste in a basic hypothetical example:
export function commentsReducer(state = initialState, action, hasPostReallyBeenAdded) {}
// elsewhere
export default function rootReducer(state = initialState, action) {
const postState = postsReducer(state.post, action);
const {hasPostReallyBeenAdded} = postState;
const commentState = commentsReducer(state.comments, action, hasPostReallyBeenAdded);
return { post : postState, comments : commentState };
}