Hey everyone, I have been trying to use the example from High Order Reducers but also using combine for child reducers, the problem that I am facing is that once I am 1 level deep into the recurse, the combine does not work anymore.
extension Reducer {
static func recurse(
_ reducer: @escaping (Self, inout State, Action, Environment) -> Effect<Action, Never>
) -> Self {
var `self`: Self!
self = Self { state, action, environment in
reducer(self, &state, action, environment)
}
return self
}
static func recurse(
_ reducer: @escaping (Reducer) -> Reducer
) -> Reducer {
var `self`: Reducer!
self = Reducer { state, action, environment in
reducer(self).run(&state, action, environment)
}
return self
}
}
struct FirstState: Equatable, Identifiable {
let id = UUID()
let isRecurseEnabled: Bool
var state: IdentifiedArrayOf<FirstState> = []
var recurseNavigation: Bool = false
var secondState: SecondState? = nil
var isShowingSecondView: Bool = false
}
indirect enum FirstAction: Equatable {
case recurse(id: FirstState.ID, action: FirstAction)
case setRecurseNavigation(isActive: Bool)
case didTapRecurse
case setSheetNavigation(isActive: Bool)
case didTapButton
case second(SecondAction)
}
let firstReducer = Reducer<
FirstState,
FirstAction,
Void
>.combine(
secondReducer
.optional()
.pullback(
state: \.secondState,
action: /FirstAction.second,
environment: { _ in }
),
.recurse { `self`, state, action, environment in
switch action {
case .didTapRecurse:
state.state = [
FirstState(isRecurseEnabled: false)
]
state.recurseNavigation = true
return .none
case .setRecurseNavigation(true):
state.recurseNavigation = true
return .none
case .setRecurseNavigation(false):
state.recurseNavigation = false
state.state = []
return .none
case let .setSheetNavigation(isActive):
state.isShowingSecondView = isActive
return .none
case .didTapButton:
state.secondState = SecondState()
state.isShowingSecondView = true
return .none
case let .second(action):
print(action)
return .none
case let .recurse(id, _):
return self.forEach(
state: \.state,
action: /FirstAction.recurse(id:action:),
environment: { $0 }
)
.run(&state, action, environment)
.map { FirstAction.recurse(id: id, action: $0)}
}
}
)
struct FirstView: View {
let store: Store<FirstState, FirstAction>
var body: some View {
WithViewStore(store) { viewStore in
NavigationView {
VStack {
Button(action: { viewStore.send(.didTapButton) }) {
Text("Tap for Second View")
.bold()
}
if viewStore.isRecurseEnabled {
Button(action: { viewStore.send(.didTapRecurse) }) {
Text("Tap for Recurse View")
.bold()
}
}
}
.sheet(
isPresented: viewStore.binding(
get: \.isShowingSecondView,
send: FirstAction.setSheetNavigation(isActive:)
)
) {
IfLetStore(
store.scope(
state: \.secondState,
action: FirstAction.second
),
then: SecondView.init(store:)
)
}
.sheet(
isPresented: viewStore.binding(
get: \.recurseNavigation,
send: FirstAction.setRecurseNavigation(isActive:)
)
) {
ForEachStore(
store.scope(
state: \.state,
action: FirstAction.recurse(id:action:)
)
) { childStore in
FirstView(store: childStore)
}
}
}
}
}
}
struct SecondState: Equatable {
}
enum SecondAction: Equatable {
case didTapButton
case changedAction
}
let secondReducer = Reducer<SecondState, SecondAction, Void> { state, action, _ in
switch action {
case .didTapButton:
return Effect(value: .changedAction)
default:
return .none
}
}
struct SecondView: View {
let store: Store<SecondState, SecondAction>
var body: some View {
WithViewStore(store) { viewStore in
NavigationView {
Button(action: { viewStore.send(.didTapButton) }) {
Text("Tap Me!!!")
.bold()
}
}
}
}
}
In this example if I tap the second screen button I will get the reducer of the second child ran and it will send an Effect to changedAction but if I am on a nested reducer this is no longer the case.