Replies: 4 comments 9 replies
-
@natemann Can you post a full, working example with steps to reproduce the problem? So long as the value type is being updated through that property, it should be observable, but maybe we're missing a piece of the puzzle. |
Beta Was this translation helpful? Give feedback.
-
I think that this is modifying the wrong thing? You shouldn't have
as childState always exists.
Here, you're directing the |
Beta Was this translation helpful? Give feedback.
-
Hey @natemann! Your sample is not working because no reducer is working on I must admit that your model is a little exotic and there are probably less intricate ways to achieve what you're looking for. I guess we can inverse the priority hierarchy. The struct Parent: ReducerProtocol {
enum Tab: Equatable {
case child
case other
}
struct State: Equatable {
init(
number: Int,
selectedTab: Parent.Tab,
section: Parent.SectionState,
child: Child.State? = nil
) {
self.number = number
self.selectedTab = selectedTab
self.section_ = section
self.child_ = child
}
var number: Int
var selectedTab: Tab
// Some private storage for the `SectionState` value
// (used when the case is `other` in this example)
private var section_: SectionState
var section: SectionState {
get {
if let child = self.child {
return .child(child)
}
return self.section_
}
set {
if case let .child(child) = newValue {
self.child = child
}
self.section_ = newValue
}
}
// Private storage that stores a fully fledged `Child.State?` to keep
// other `Child.State` properties than `number` (here, there are none,
// but I'm implementing it as if it wasn't the case).
private var child_: Child.State?
var child: Child.State? {
get {
// Pick the private storage:
var child = self.child_
// Inject self.number:
child?.number = self.number
// Return the updated value:
return child
}
set {
// Save the new `Child.State?` in the private storage:
self.child_ = newValue
if let newValue {
// Extract self.number:
self.number = newValue.number
}
}
}
}
enum Action {
case section(SectionAction)
case select(Tab)
}
enum SectionState: Equatable {
case child(Child.State)
case other(Other.State)
}
enum SectionAction {
case child(Child.Action)
case other(Other.Action)
}
var body: some ReducerProtocol<State, Action> {
Scope(state: \.section, action: /Action.section) {
// Alternative to `EmptyReducer().ifCaseLet().ifCaseLet()…`
Scope(state: /SectionState.child, action: /SectionAction.child) {
Child()
}
Scope(state: /SectionState.other, action: /SectionAction.other) {
Other()
}
}
Reduce { state, action in
switch action {
case .section:
return .none
case .select(.child):
state.selectedTab = .child
state.section = .child(.init(number: state.number))
return .none
case .select(.other):
state.selectedTab = .other
state.section = .other(.init(number: 0))
return .none
}
}
}
} And this reducer updates the parent value as expected. Please note that I'm underscoring private values with a trailing underscore because it composes more nicely with property wrappers (that are already using a leading underscore for the wrapped storage) and with CoreData (that prohibits values starting with an underscore). But you can of course use a leading underscore (or whatever convention) to denote private storage. Of course, this example can be simplified by removing the One alternative would be to remove the reducer from Reduce { … }
.ifLet(\.child, action: /Action.section .. /SectionAction.child) {
Child()
} but it forces us to compose |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
it seems common practice to have a child state as a computed property to update a parent's property. Is the possible when the child state is part of a
SwitchStore
. For Example:The
set
never gets called whenchildState
is updated. My guess is becauseIfCaseLet
in theReducer
usesCasePath
instead of a theoreticalWritableCasePath
. Is this correct, or is there a way I can getset
to be called?Beta Was this translation helpful? Give feedback.
All reactions