I once attended a talk about advanced Redux (another unidirectional architecture similar to SCA). It contained interesting and powerful ideas relating to taking advantage of the action layer. Patterns like "mapping", "splitting" and "enhancing" actions. This "higher order action" approaches were implemented at a middleware layer that doesn't exist in SCA but I imagine it could be replicated in the effects layer. Regardless of how implemented, it might be worth thinking about the utility of altering actions on their pathway to their final reducer and I'm curious about people's thoughts.
In practice, I've found a handful of use cases where this more dynamic treatment of how actions flow through an app might prove beneficial.
ex1)
Often I'll have (a) some top level modal state, and (b) a view that presents modally and dispatches a terminal action. Here, I can wed the terminal action to clearing the modal like this:
switch actionFromModal {
case let .selectedEntity(_):
state.modal = nil
}
but I can't help but notice we're disregarding the associated value.
also, lets say .selectedEntity
is nested: .entities(.subEntity(.selected(id)))
. now we need to introduce a verbose pattern matcher, which i feel, knows to much about the reducer/action structure and is likely to change.
the benefit of action splitting here would be to send the more focused .clearModal
action alongside .selectedEntity
.
ex2)
Another example is selecting items after creating them. This is a common idiom. Without the capacity to do action splitting its handled implicitly in the reducer logic:
case .insertNew(entity):
state.deselectAll()
var entity = entity
entity.isSelected = true
state.append(entity)
Now lets say we have a .select(id: ID)
action that corresponds to something like this:
case let .select(id):
state.deselectAll()
state.selectEntity(at: id)
We don't have a way of composing these at the action level. Because we don't have the ability to generate N actions from 1, we must create a new action .insertNewEntityAndSelect
.
the benefit of action splitting here is clearly that you could dispatch .insert
and .select
together.
--