浅用高阶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 协议 ,转载请注明出处!