浅用高阶Redux原理实现日志流水--实现篇
浅用高阶 Redux 原理实现日志流水–实现篇
Demo B 要实现低代码操作流水记录,实现撤回重做的功能,一开始的想法比较简单,直接在 action 中完善撤回重做功能,但是后面参考官方的 redux ,其实有个高级的写法,也就是高阶 Redux,可以实现可插件式的可撤退重做日志。具体高阶 Redux 介绍,可以移步到,文章:Redux 官方实现撤销重做 – 解析
01 实现撤回重做的方案
- 数据快照式,也就是将每次操作完之后,数据状态进行保存,从而形成历史记录- 优点:通用性广,实现容易,可封装成组件成可插拔插件
- 缺点:假若数据结构复杂庞大,随着操作历史记录变多,导致数据量冗余,不太好管理
 
- 路径新旧值式,单一数据源,对每次操作后,将操作数据的路径、新值和旧值作为记录进行保存- 优点:只需记录操作路径和新旧值,减少大量的内存消耗,方便管理数据
- 缺点:实现比较难, 需要对 action 的传入数据的结构有所要求(路径、新旧值)
 
下文将采用的是路劲新旧值式的方案
02 数据结构的设计
| 1 |  | 
其中,
- histories: 用于存放记录数组
- historyIndex: 游标,用于标识当前历史记录的指针
- current:存放业务 reducer 返回最新的业务 state
03 撤销重做算法
撤销 undo
- 判断 historyIndex的值,从而判断撤退进行到什么状态。- 0 ,说明已经执行到栈底了
- -1,未发生撤退操作
- != -1 && != 0,正在进行撤退操作
 

- 执行撤回操作- 若 newValue === null, 为删除操作,对应撤回操作 => 新增操作
- 若 oldValue === null, 为新增操作,对应撤回操作 => 删除操作
- 若 newValue !== null && oldValue !== null,为更新操作,对应撤回操作 => 更新操作
 
- 若 
重做 redo
- 判断 - historyIndex的值,是否已经发生撤回操作 
- 执行重做操作 - 若 newValue === null, 为删除操作,对应重做操作 => 删除操作
- 若 oldValue === null, 为新增操作,对应撤重回操作 =>新增操作
- 若 newValue !== null && oldValue !== null,为更新操作,对应重做操作 => 更新操作
 
- 若 
- 最后,判断 - historyIndex是否到达栈顶- 到达栈顶,重置游标historyIndex = -1
- 未到达栈顶,historyIndex ++1
 
- 到达栈顶,重置游标
其他业务 Action
- 特殊情况:当在发生撤退重做时,若发生其他业务 Action 操作,应该把当前游标后面的记录清空,并推入新的业务 Action 操作 
- 执行其他业务的 Action,委托给业务的 reducer 进行处理,并将业务 reducer 返回新的 业务 state 存储到 current 
- 高阶 reducer 接收 - include数组参数,用于判断是否将这个操作加入撤退重做记录中- 属于,将其封装成记录,并推入 histories
- 不属于,无处理
 
- 属于,将其封装成记录,并推入 
- 额外操作:当高阶 reducer 接收 - limit参数,用于限制记录的最大长度- 默认, limit = false,不进行记录长度的限制
- limit 为数字,且大于 0。对记录进行限制
 
- 默认, 
03 实现源码
| 1 |  | 
使用方式:
| 1 |  | 
04 不足之处
- 还未解决,封装一个方法,可以使得业务 action 无需按照高阶 reducer 的需要的数据格式进行数据包装,做到真正地可插拔式
- 提供更多的参数,以便做到职责分明,减少耦合依赖
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!