参照ReSwift改写的Objective-C版本Redux。
git clone https://github.com/DanboDuan/ReduxDemo.git
cd ReduxDemo/Eample
pod install
open ReduxDemo.xcworkspace
在把ReSwift改成Objective-C
版的过程中一些个人体验。
- Swift泛型更加强大
- Swift闭包更加强大
- 但是Swift复杂闭包嵌套可读性比较差(个人理解,不寻求认同)
- 单一数据源
- State 是只读的
- 使用纯函数来执行修改
- 直接copy代码,并用你的项目前缀重命名文件、类和协议。
- pod
pod 'Redux',:git => 'https://github.com/DanboDuan/ReduxDemo.git',:branch => 'master',:tag => '1.3.0'
- 请参考Demo
- 异步操作参照Thunk
- CombineReducers
- ReduceReducers
-
State
- State应用程序状态,或者说数据,Model。
- State是只读的,且Immutable
- 唯一改变 state 的方法就是触发 action
-
Action
- Action 是一个用于描述已发生事件的普通对象
- Action 不能修改State ,Action 描述一个行为然后经过 Reducer 修改 State
-
Store
- 维护应用 State 并支持访问 State
- 一个 State 存在唯一的一个 Store 中
- 支持 Subscribers 订阅 Store 的变更
- 支持监听 Action 的分发,更新状态
-
Reducer
- 纯函数,接收先前的 State 和 Action,并返回新的 State。
- Reducer描述 Action 如何改变 State 的,这也是State唯一会发生变化的地方。
- 一个Store可以有多个Reducer来处理Action和State
- 一个Reducer可以处理多个Action,一个Action也可以经过处理多个Reducer处理
-
Middleware
- 在 Action 达到 Store 之前的中间层,可以做一些额外的操作
- 不可以修改 State
-
Subscribers
- 通过 Store 来 订阅 State 的变化
- 接受 State 变化的通知并做相应的展示
- Subscribers 可以是一个View,ViewController 或者一个普通的 Obeserver
- 状态管理,统一维护管理应用状态
- 某一状态只有一个可信数据来源
- 可预测性, 一个 State 经过一个 Action 在 某个 Reducer 中更新后会变成什么样
- 可追溯,可重演
- 适用场景:多交互、多数据源
- 结构复杂,代码量增加,简单的场景其实不需要 Redux
- 性能问题
这个案例比较简单,但是已经具备了Redux的绝大部分核心组件。
两个Action,描述数字增加or减小
@interface CounterIncrAction : NSObject<Action>
@end
@interface CounterDecrAction : NSObject<Action>
@end
数字当前的状态
@interface CounterState : NSObject<State>
@property (nonatomic, assign, readonly) NSInteger number;
+ (instancetype)stateWithNumber:(NSInteger)number;
@end
纯函数,根据输入的action和state返回更新后的state
Reducer CounterReducer = ^CounterState * (id<Action> action, CounterState *state) {
if (state == nil) {
return [CounterState stateWithNumber:0];
}
if ([action isKindOfClass:[CounterIncrAction class]]) {
return [CounterState stateWithNumber:state.number + 1];
}
if ([action isKindOfClass:[CounterDecrAction class]]) {
return [CounterState stateWithNumber:state.number - 1];
}
return state;
};
接受CounterState更新,并展示出来。
由于demo简易,直接在CounterViewController中创建 Store
@interface CounterViewController () <Subscriber>
- (void)updateState:(CounterState *)state;
@end
@implementation CounterViewController
- (instancetype)init {
self = [super init];
if (self) {
self.store = [[Store alloc] initWithReducer:CounterReducer
state:[CounterState stateWithNumber:12]
middlewares:@[ActionLogger, StateLogger] /*autoSkipRepeats:NO*/];
// if autoSkipRepeats NO, the same number with state will repeatly callback
}
return self;
}
- (void)buttonClick:(UIButton *)sender {
NSString *type = sender.currentTitle;
if ([type isEqualToString:@"-"]) {
[self.store dispatch:[CounterDecrAction new]];
} else {
[self.store dispatch:[CounterIncrAction new]];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.store subscribe:self];
}
- (void)updateState:(CounterState *)state {
self.label.text = [NSString stringWithFormat:@"%zd",state.number];
}
Middleware ActionLogger = ^DispatchFunctionChain (DispatchFunction dispatch, GetState getState) {
return ^DispatchFunction (DispatchFunction next) {
return ^(id<Action> action) {
printf("\nACTION %s\n", [action description].UTF8String);
next(action);
return action;
};
};
};
Middleware StateLogger = ^DispatchFunctionChain (DispatchFunction dispatch, GetState getState) {
return ^DispatchFunction (DispatchFunction next) {
return ^(id<Action> action) {
next(action);
printf("\nSTATE %s\n", [getState() description].UTF8String);
return action;
};
};
};
- 整个App可以共用一个 Store 也可以每个模块单独使用一个 Store,但是每个Store都只有唯一一个 State
- State 可以简单也可以复杂
- Subscribers 可以订阅完整的 State 也可以订阅 State 的某一部分,或者某个Transform
- State
- Action
- Store
- Middleware
- Thunk(异步操作)
- CombineReducers
- ReduceReducers
- Scheduled Dispatch
使用 MIT 协议,详情见LICENSE文件。