Scope vs reduce(into:action:) #3004
-
Hi everyone! @Reducer
struct MyFeature {
@ObservableState
struct State {
var names = [String]()
var newName: String?
}
enum Action: ViewAction {
case `internal`(Internal)
case view(View)
enum Internal {
case startFetchingNames
case namesFetched([String])
}
enum View {
case newButtonTapped
}
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case let .internal(internalAction): reduceInternal(into: &state, action: internalAction)
case let .view(viewAction): reduceView(into: &state, action: viewAction)
}
}
}
private func reduceInternal(into state: inout State, action: Action.Internal) -> Effect<Action> {
switch action {
case .startFetchingNames:
return .run { send in
try await Task.sleep(for: .seconds(1))
await send(.internal(.namesFetched([
"First name",
"Second name"
])))
}
case let .namesFetched(names):
state.names = names
return .none
}
}
private func reduceView(into state: inout State, action: Action.View) -> Effect<Action> {
switch action {
case .newButtonTapped:
state.newName = "My new cool name"
return .none
}
}
} This is the pattern I use to make reducers a little more readable. In this form there's any additional work needed from CPU to break a reducer down. But I was thinking about breaking down the reducer with a @Reducer
struct MyFeature {
@ObservableState
struct State {
var names = [String]()
var newName: String?
}
enum Action: ViewAction {
case `internal`(Internal)
case view(View)
enum Internal {
case startFetchingNames
case namesFetched([String])
}
enum View {
case newButtonTapped
}
}
var body: some ReducerOf<Self> {
Scope(state: \.self, action: \.internal) {
`internal`
}
Scope(state: \.self, action: \.view) {
view
}
}
@ReducerBuilder<State, Action.Internal>
private var `internal`: some Reducer<State, Action.Internal> {
Reduce { state, action in
switch action {
case .startFetchingNames:
return .run { send in
try await Task.sleep(for: .seconds(1))
await send(.namesFetched([
"First name",
"Second name"
]))
}
case let .namesFetched(names):
state.names = names
return .none
}
}
}
@ReducerBuilder<State, Action.View>
private var view: some Reducer<State, Action.View> {
Reduce { state, action in
switch action {
case .newButtonTapped:
state.newName = "My new cool name"
return .none
}
}
}
} Right away I can tell that |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Hi @vyachesIavskiy, there really shouldn't be too much of a performance concern to using However, I still don't think it's a good idea to reduce on the @ReducerBuilder<State, Action.View>
private var view: some Reducer<State, Action.View> { Such a reducer can only send view actions from effects, which is usually not what you would do if you are separating your view actions from your "internal" actions. I don't really think there is a concept of a "view" only reducer. The separation of the actions is really only helpful in the view layer. |
Beta Was this translation helpful? Give feedback.
Hi @vyachesIavskiy, there really shouldn't be too much of a performance concern to using
Scope
. The biggest cost is to using key paths in Swift, which unfortunately are a little bit slower than direct property access, but hopefully that will be fixed some day.However, I still don't think it's a good idea to reduce on the
State
andAction.View
as you are doing in theview
computed property:Such a reducer can only send view actions from effects, which is usually not what you would do if you are separating your view actions from your "internal" actions. I don't really think there is a concept of a "vie…