SwiftUI Redux 中子 Reducer 调用其他 Reducer 的方法
在 SwiftUI Redux 架构中,有几种常见的方式来实现子 Reducer 之间的通信和调用。
1. 通过 Action 链式调用
这是最符合 Redux 哲学的方式,通过分发 Action 来触发其他 Reducer:
// 定义 Action
enum AppAction {
case user(UserAction)
case settings(SettingsAction)
case crossModuleAction(CrossModuleAction)
}
enum CrossModuleAction {
case userUpdatedAndNeedSettingsRefresh
}
// 主 Reducer
func appReducer(state: inout AppState, action: AppAction) -> Void {
switch action {
case .user(let userAction):
userReducer(state: &state.user, action: userAction)
// 检查是否需要触发其他模块的 Action
if case .userProfileUpdated = userAction {
// 分发跨模块 Action
return appReducer(state: &state, action: .crossModuleAction(.userUpdatedAndNeedSettingsRefresh))
}
case .settings(let settingsAction):
settingsReducer(state: &state.settings, action: settingsAction)
case .crossModuleAction(let crossAction):
handleCrossModuleAction(state: &state, action: crossAction)
}
}
// 处理跨模块 Action
func handleCrossModuleAction(state: inout AppState, action: CrossModuleAction) {
switch action {
case .userUpdatedAndNeedSettingsRefresh:
// 调用 settings reducer 的刷新方法
settingsReducer(state: &state.settings, action: .refreshBasedOnUserChange)
}
}
2. 使用 Effect 系统(类似 Redux-Observable)
实现一个 Effect 系统来处理副作用和跨 Reducer 调用:
struct Effect<Action> {
let run: (@escaping (Action) -> Void) -> Void
}
// Reducer 返回 (state, effect) 元组
typealias Reducer<State, Action> = (inout State, Action) -> [Effect<Action>]
func userReducer(state: inout UserState, action: UserAction) -> [Effect<AppAction>] {
switch action {
case .loginSuccess:
state.isLoggedIn = true
// 返回一个 effect 来触发其他 reducer
return [Effect { dispatch in
dispatch(.settings(.loadUserSpecificSettings))
dispatch(.notifications(.setupForUser)))
}]
default:
return []
}
}
3. 组合式 Reducer 设计
创建可以相互调用的组合式 Reducer:
protocol Reducible {
associatedtype State
associatedtype Action
static func reduce(state: inout State, action: Action) -> SideEffects<Action>
}
enum SideEffects<Action> {
case none
case effect(Action)
case effects([Action])
func map<T>(_ transform: (Action) -> T) -> SideEffects<T> {
// 实现 map 逻辑
}
}
// 用户模块 Reducer
enum UserReducer: Reducible {
static func reduce(state: inout UserState, action: UserAction) -> SideEffects<AppAction> {
switch action {
case .updateProfile:
state.profile = action.newProfile
// 返回需要触发的其他 Action
return .effect(.settings(.updateUserProfile(action.newProfile)))
default:
return .none
}
}
}
// 主 Reducer 组合器
struct AppReducer {
static func reduce(state: inout AppState, action: AppAction) {
let sideEffects: SideEffects<AppAction>
switch action {
case .user(let userAction):
sideEffects = UserReducer.reduce(state: &state.user, action: userAction)
case .settings(let settingsAction):
sideEffects = SettingsReducer.reduce(state: &state.settings, action: settingsAction)
}
// 处理 side effects
handleSideEffects(sideEffects, state: &state)
}
private static func handleSideEffects(_ sideEffects: SideEffects<AppAction>, state: inout AppState) {
switch sideEffects {
case .effect(let action):
reduce(state: &state, action: action)
case .effects(let actions):
actions.forEach { reduce(state: &state, action: $0) }
case .none:
break
}
}
}
4. 使用 Middleware 进行通信
通过 Middleware 来拦截 Action 并触发其他 Action:
struct CrossModuleMiddleware: Middleware {
typealias State = AppState
func process(state: State, action: AppAction, dispatch: @escaping (AppAction) -> Void) {
switch action {
case .user(.logout):
// 用户登出时,清理所有相关模块
dispatch(.settings(.resetToDefaults))
dispatch(.notifications(.clearAll))
dispatch(.cart(.clearCart))
case .user(.loginSuccess(let user)):
// 用户登录成功,初始化相关模块
dispatch(.settings(.loadUserPreferences(user.id)))
dispatch(.notifications(.registerDeviceToken))
default:
break
}
}
}
// Middleware 协议
protocol Middleware {
associatedtype State
func process(state: State, action: AppAction, dispatch: @escaping (AppAction) -> Void)
}
5. 依赖注入式 Reducer
为 Reducer 提供调用其他 Reducer 的能力:
struct ReducerEnvironment {
let dispatch: (AppAction) -> Void
let getState: () -> AppState
}
func userReducer(environment: ReducerEnvironment) -> (inout UserState, UserAction) -> Void {
return { state, action in
switch action {
case .purchaseCompleted(let product):
state.purchasedProducts.append(product)
// 通过 environment 调用其他 reducer
environment.dispatch(.settings(.unlockFeature(product.featureId)))
environment.dispatch(.analytics(.trackPurchase(product)))
default:
break
}
}
}
6. 实际应用示例
// 完整的应用示例
class Store: ObservableObject {
@Published private(set) var state: AppState
private let reducer: (inout AppState, AppAction) -> Void
private let middlewares: [Middleware]
init(state: AppState, reducer: @escaping (inout AppState, AppAction) -> Void, middlewares: [Middleware]) {
self.state = state
self.reducer = reducer
self.middlewares = middlewares
}
func dispatch(_ action: AppAction) {
// 先执行 middleware
middlewares.forEach { $0.process(state: state, action: action, dispatch: self.dispatch) }
// 执行 reducer
reducer(&state, action)
}
}
// 在 SwiftUI 中使用
struct ContentView: View {
@EnvironmentObject var store: Store
var body: some View {
Button("Login") {
store.dispatch(.user(.login(username: "user", password: "pass")))
// 这会自动触发相关的 settings 和 notifications actions
}
}
}
最佳实践建议
- 优先使用 Action 链式调用 - 最符合 Redux 单向数据流原则
- 对于复杂副作用使用 Effect 系统 - 更适合异步操作和复杂逻辑
- 避免直接函数调用 - 保持 Reducer 的纯净性和可测试性
- 使用 Middleware 处理横切关注点 - 如日志、分析、错误处理等
选择哪种方式取决于你的应用复杂度和团队偏好。简单的应用可以使用基本的 Action 链式调用,复杂应用可以考虑实现完整的 Effect 系统。