Tree-Based style navigation send action from parent to child

When implementing the Tree-Based navigation style, it is possible to send actions to a child reducer from the parent reducer.

For context, here is an example of the code i'm using:
And I'm using TCA 0.57.0 right now

extension ChildOne {
    enum Action {
        case updateStateWhenViewAppear
    }
}

extension ParentReducer {
struct Destination: Reducer {
    enum State {
        case childOne(ChildOne.State)
        case childTwo(ChildTwo.State)
    }

    enum Action {
        case childOne(ChildOne.Action)
        case childTwo(ChildTwo.Action)
    }

    var body: some ReducerOf<Self> { ... }
}
}

extension ParentReducer {
    struct State {
        @PresentationState var destination: ParentReducer.Destination
    }

    enum Action {
        case destination(PresentationAction<ParentReducer.Action>)
    }
}

And this is the code that I have to call an action from the cases from the Destination struct, but this doesn't work for me:

return ChildOne()
          .reduce(into: &state.destination, action: .updateStateWhenViewAppear)
          .map(Action.destination)

Is there is any suggestion for how to call a child action, from the parent reducer, using a Tree-Based navigation style?

It is possible to invoke the child reducer directly (with caveats, below), but you need to fully transform things. The Destination contains more than ChildOne, so you need to further describe that childOne domain in the transformation:

switch state.destination {
// dive further into state.destination
case .childOne(var childState):
  // ensure state changes are propagated back
  defer { state.destination = .childOne(childState) }
  return ChildOne()
    .reduce(into: &childState, action: .updateStateWhenViewAppear)
    // bundle things up into 2 layers
    .map { .destination(.childOne($0)) }
// ...
}

Note that running the reducer locally like this will mean that no parent reducer can "observe" the action and react to it, which may not be what you want. If you want to send an action through the entire system, you can usually do so much more simply from the parent:

return .send(.destination(.childOne(.updateStateWhenViewAppear)))

(This of course is more expensive since it goes through the entire reducer, which may not be a big deal, but is something to keep in mind.)

1 Like

Thanks, it works! :smiley: