I just started working through the most recent TCA videos going through the Standups app, I have been rebuilding an app I made in Vanilla SwiftUI in TCA as I learn from the series.
I am building a form using text fields and I am unsure if I am using the onChange modifier against the BindingReducer correctly. I've created a minimal example to show what I've come up with.
I have the following questions:
-
Is this the correct usage of the BindingReducer onChange modifier? It seems a little weird to process the logic as an effect when it is quick logic that isn't a side effect.
-
This is just one field, my form will have at least three such fields with additional pre-processing behavior like this, is this the correct approach?
example:
struct OnChangeExampleView: View {
let store: StoreOf<OnChangeExampleFeature>
var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
TextField("myField", text: viewStore.$myField)
Text("\(viewStore.myFieldDouble)")
}
}
}
@Reducer
struct OnChangeExampleFeature: Reducer {
struct State: Equatable {
@BindingState var myField = ""
var myFieldDouble: Double
}
enum Action: BindableAction {
case binding(BindingAction<State>)
case setMyFieldDouble(Double)
}
var body: some ReducerOf<Self> {
BindingReducer()
.onChange(of: \.myField) { oldValue, newValue in
Reduce { state, action in
.run { send in
if let value = Double(newValue.filter("0123456789.".contains)) {
await send(.setMyFieldDouble(value))
}
}
}
}
Reduce { state, action in
switch action {
case .binding:
return .none
case let .setMyFieldDouble(val):
state.myFieldDouble = val
return .none
}
}
}
}
#Preview {
OnChangeExampleView(
store: Store(initialState: OnChangeExampleFeature.State(myFieldDouble: 0.0)) {
OnChangeExampleFeature()
._printChanges()
}
)
}