hbayramov
(Huseyn Bayramov)
1
Hi guys, I stuck with parent-child actions. I have child State and Actions inside Parent domain and I have added Scope like this:
var body: some Reducer<State, Action> {
Scope(state: \.paymentsState, action: /FavoritesReducer.Action.paymentsAction) {
FavoritesPaymentReducer()
}
Reduce(self.core)
}
and inside parent view
FavoritePaymentsView(
store: self.store.scope(state: \.paymentsState, action: FavoritesReducer.Action.paymentsAction),
Inside payments actions (child actions) I want to ignore favorite selected and handle this action inside parent domain. It actually works when I printChanges. I mean it triggers actions and changes state to show alert, but Alert not showing up. I tried to call it with closure (I mean triggering parent with closure from child view) and it shows. How can I ignore this action inside child and handle in parent to show it properly. Because, it's a segmented view and have a lot of common alerts.
Thank you
otondin
(Gabriel Tondin)
2
hi @hbayramov ! Maybe you want to catch on parent reducer the child action, like:
var body: some Reducer<State, Action> {
Scope(state: \.paymentsState, action: /FavoritesReducer.Action.paymentsAction) {
FavoritesPaymentReducer()
}
Reduce { state, action in
switch action {
case .paymentsAction(.somePaymentAction):
// catch a specific child action
return .none
case .paymentsAction:
// catch-all child actions
return .none
}
}
}
hbayramov
(Huseyn Bayramov)
3
Hi @otondin, thank you for reply. I actually did that way. Inside core function of parent domain i have added it like this:
case .paymentsAction(.favoriteSelected(let favorite)):
state.selectedFavorite = favorite
return self.handleSelectedFavorite(state: &state)
case .paymentsAction(.favoriteActionSelected(let favorite)):
state.selectedFavorite = favorite
return .send(.toggleShowSelectActionsView(true))
default:
return .none
and it trigger and updates state. But alert not showing up in parent view.
.floatingSheet(
isPresented: viewStore.binding(
get: \.showSelectActionsView,
send: FavoritesReducer.Action.toggleShowSelectActionsView
)
)
But when triggering parent action like this it works
FavoritePaymentsView(
store: self.store.scope(state: \.paymentsState, action: FavoritesReducer.Action.paymentsAction),
favoriteActionSelected: { favorite in
viewStore.send(.favoriteActionSelected(favorite))
},
favoriteSelected: { favorite in
viewStore.send(.favoriteSelected(favorite))
}
)
I just don't want to trigger via closure.
otondin
(Gabriel Tondin)
4
I see, but do you have the catch-all child actions case too right?
hbayramov
(Huseyn Bayramov)
5
No, I just need to catch only two actions. Rest will be handled inside Child domain. These two actions common between childs. That's why I don't want to add same alerts for two different views.
otondin
(Gabriel Tondin)
6
@hbayramov I see, so try to implement the catch-all child actions case after your specific actions cases like this:
case .paymentsAction(.favoriteSelected(let favorite)):
state.selectedFavorite = favorite
return self.handleSelectedFavorite(state: &state)
case .paymentsAction(.favoriteActionSelected(let favorite)):
state.selectedFavorite = favorite
return .send(.toggleShowSelectActionsView(true))
case .paymentsAction:
return .none
hbayramov
(Huseyn Bayramov)
7
I have added this instead of default return none still the same, not showing up
otondin
(Gabriel Tondin)
8
Can you share, for a chance, your entire feature domain (parent and child) implementations?
hbayramov
(Huseyn Bayramov)
9
Parent
final class FavoritesReducer: Reducer {
struct State: Equatable {
public var selectedSegment: FavoriteSegmentView.Segment
public var isLoading: Bool
public var error: Error?
public var searchText: String
public let actions: [SelectActionsType]
public var showSelectActionsView: Bool
public var showUpdateView: Bool
public var showDeleteView: Bool
public var selectedNewName: String?
public var selectedFavorite: FavoritesUIModel.Favorite?
public var paymentsState: FavoritesPaymentReducer.State
public init() {
self.selectedSegment = .payments
self.isLoading = false
self.searchText = .empty()
self.actions = [.update, .changePaymentType, .delete]
self.showSelectActionsView = false
self.showUpdateView = false
self.showDeleteView = false
self.paymentsState = FavoritesPaymentReducer.State()
}
}
enum Action: Equatable {
case back
case segmentChanged(FavoriteSegmentView.Segment)
case searchTextChanged(String)
case favoriteActionSelected(FavoritesUIModel.Favorite)
case favoriteSelected(FavoritesUIModel.Favorite)
case toggleShowSelectActionsView(Bool)
case toggleShowUpdateView(Bool)
case toggleShowDeleteView(Bool)
case selectedAction(SelectActionsType)
case dismissError
case none
case paymentsAction(FavoritesPaymentReducer.Action)
}
var body: some Reducer<State, Action> {
Scope(state: \.paymentsState, action: /FavoritesReducer.Action.paymentsAction) {
FavoritesPaymentReducer()
}
Reduce(self.core)
}
private func core(
into state: inout State,
action: Action
) -> Effect<Action> {
switch action {
case .back:
router.dismissCoordinator()
return .none
case .segmentChanged(let segment):
guard !state.isLoading else { return .none }
state.selectedSegment = segment
return .send(.getFavorites(segment))
case .searchTextChanged(let text):
return checkSearch(text: text, state: &state)
case .favoriteActionSelected(let favorite):
state.selectedFavorite = favorite
return .send(.toggleShowSelectActionsView(true))
case .favoriteSelected(let favorite):
state.selectedFavorite = favorite
return self.handleSelectedFavorite(state: &state)
case .toggleShowSelectActionsView(let newValue):
state.showSelectActionsView = newValue
return .none
case .toggleShowUpdateView(let newValue):
state.showUpdateView = newValue
return .none
case .toggleShowDeleteView(let newValue):
state.showDeleteView = newValue
return .none
case .selectedAction(let action):
return self.handleActions(with: action, state: &state)
case .dismissError:
state.error = nil
return .none
case .none:
return .none
case .paymentsAction(.favoriteSelected(let favorite)):
state.selectedFavorite = favorite
return self.handleSelectedFavorite(state: &state)
case .paymentsAction(.favoriteActionSelected(let favorite)):
state.selectedFavorite = favorite
return .send(.toggleShowSelectActionsView(true))
case .paymentsAction:
return .none
}
}
}
Child
final class FavoritesPaymentReducer: Reducer {
struct State: Equatable {
public var isLoading: Bool
public var searchText: String
public var showDebtInfoView: Bool
public var debtCheckDate: String
public var isDebtCheckAvailable: Bool
public var lastRequestTime: Date
public init() {
self.isLoading = false
self.searchText = .empty()
self.showDebtInfoView = false
self.debtCheckDate = .empty()
self.isDebtCheckAvailable = false
self.lastRequestTime = UserDefaults.lastDebtRequestTime
}
}
enum Action: Equatable {
case searchTextChanged(String)
case favoriteActionSelected(FavoritesUIModel.Favorite)
case favoriteSelected(FavoritesUIModel.Favorite)
case toggleDebtInfoView(Bool)
case checkIfDebtIsAvailable
case none
}
var body: some Reducer<State, Action> {
Reduce { [weak self] state, action in
guard let self = self else { return .none }
switch action {
case .searchTextChanged(let text):
return self.handleSearchForPayments(with: text, state: &state)
case .toggleDebtInfoView(let value):
state.showDebtInfoView = value
return .none
case .checkIfDebtIsAvailable:
state.isDebtCheckAvailable = interactor.isDebtCheckAvailable
return .none
case .none:
return .none
default:
return .none
}
}
}
}
otondin
(Gabriel Tondin)
10
@hbayramov thanks! I see, I don't know if you're working with SwiftUI or UIKit, but are you aware of the BindingState feature? It might help you get reactiveness easier...
1 Like
hbayramov
(Huseyn Bayramov)
11
I am working with SwiftUI. I will try it. Thanks @otondin.
hbayramov
(Huseyn Bayramov)
12
@otondin I have tried it with @BindingState, it worked
Thank you man. Thank you for your time.
1 Like