Hi, I've struggled for this scenario for a while and still have not found the best practice yet.
For example, I have a view hierarchy A -> B, and each has its owns State/Action/Reducer.
Assume that you have an array [Model], and you can do operation to each Model at B view. A ForEachStore
is used here apparently. The result of the operation comes from a delegate which is received at A in the form of a long Effect.
This means that ReducerA is able to mutate the state of the Model I operate at ViewB.
After the operation executed in ViewB and the value of the Model is modified in ReducerA (because only ReducerA handles the delegate Effect), I want to trigger another ActionB (executed in ReducerB) so that I can do some extra logic at ViewB.
The question is, how can I make it happen?
Now I have two workarounds:
-
Move all these "extra logic" to ReducerA, which seems really bad because they should belong to ReducerB.
-
Post a notification from ReducerA and ReducerB listens to this notification and execute those "extra logic"
It seems to me that both are not very good. Just wonder what's the best practice for this requirement. Is there any way for reducer to "push down" actions as opposed to Reducer.pullback
?
An example code to illustrate the question:
// MARK: Client of delegate
struct SomeDelegateClient {
enum Action: Equatable {
case update(id: UUID, state: Model.State)
}
var open: (AnyHashable) -> Effect<Action, Never>
}
// MARK: Parent State/Action/Environment/Reducer (A)
struct ParentState: Equatable {
var children = IdentifiedArrayOf<Model>()
}
enum ParentAction: Equatable {
case onAppear
case delegate(SomeDelegateClient.Action)
}
struct ParentEnv {
var open: (AnyHashable) -> Effect<SomeDelegateClient.Action, Never>
}
let parentReducer = Reducer<ParentState, ParentAction, ParentEnv> { state, action, env in
struct DelegateID: Hashable {}
switch action {
case .onAppear:
return env.open(DelegateID()).map(ParentAction.delegate)
case let .delegate(.update(id, newState)):
state.children[id: id]?.state = newState
// [Question] How to send `ChildAction.didUpdate` to child store so that "extra logic" defined in childReducer can be executed.
// Or any other solution? TCA allows us to mutate child state in the parent scope but how can we `push down` the action as opposed to Reducer.pullback ?
return .none
}
}
// MARK: Child State/Action/Environment/Reducer (B)
struct Model: Equatable, Identifiable {
enum State: Equatable {
case standby, completed
}
var state: State = .standby
var id = UUID()
}
enum ChildAction: Equatable {
case didUpdate
}
let childReducer = Reducer<Model, ChildAction, Void> { state, action, _ in
switch action {
case .didUpdate:
// The "extra logic" to be executed
return .none
}
}