前面的 State 类用于表示某个时刻应用的状态,接下来我们定义 AppHistory 类用来表示应用的历史记录。同样的,我们仍然使用 Immutable Record 来定义历史记录。其中 state 字段用来表达当前的应用状态,list 字段用来存放所有的 action,而 index 字段用来记录最近的 applied-action 的下标。应用的历史状态可以通过 undo/redo 方法计算得到。apply 方法用来向 AppHistory 中添加并执行具体的 Action。具体代码如下:
// AppHistory.ts
const emptyAction = Symbol('empty-action')
export const undo = Symbol('undo')
export type undo = typeof undo // TypeScript2.7之后对symbol的支持大大增强
export const redo = Symbol('redo')
export type redo = typeof redo
const AppHistoryRecord = Record({
// 当前应用状态
state: new State(),
// action 列表
list: List<Action>(),
// index 表示最后一个applied-action在list中的下标。-1 表示没有任何applied-action
index: -1,
})
export default class AppHistory extends AppHistoryRecord {
pop() { // 移除最后一项操作记录
return this
.update('list', list => list.splice(this.index, 1))
.update('index', x => x - 1)
}
getLastAction() { return this.index === -1 ? emptyAction : this.list.get(this.index) }
getNextAction() { return this.list.get(this.index + 1, emptyAction) }
apply(action: Action) {
if (action === emptyAction) return this
return this.merge({
list: this.list.setSize(this.index + 1).push(action),
index: this.index + 1,
state: action.next(this.state),
})
}
redo() {
const action = this.getNextAction()
if (action === emptyAction) return this
return this.merge({
list: this.list,
index: this.index + 1,
state: action.next(this.state),
})
}
undo() {
const action = this.getLastAction()
if (action === emptyAction) return this
return this.merge({
list: this.list,
index: this.index - 1,
state: action.prev(this.state),
})
}
}
第四步:添加「撤销重做」功能
假设应用中的其他代码已经将网页上的交互转换为了一系列的 Action 对象,那么给应用添上「撤销重做」功能的大致代码如下:
type HybridAction = undo | redo | Action
// 如果用Redux来管理状态,那么使用下面的reudcer来管理那些「需要历史记录的状态」
// 然后将该reducer放在应用状态树中合适的位置
function reducer(history: AppHistory, action: HybridAction): AppHistory {
if (action === undo) {
return history.undo()
} else if (action === redo) {
return history.redo()
} else { // 常规的 Action
// 注意这里需要调用prepare方法,好让该action「准备好」
return action.prepare(history).apply(action)
}
}
// 如果是在 Stream/Observable 的环境下,那么像下面这样使用 reducer
const action$: Stream<HybridAction> = generatedFromUserInteraction
const appHistory$: Stream<AppHistory> = action$.fold(reducer, new AppHistory())
const state$ = appHistory$.map(h => h.state)
// 如果是用回调函数的话,大概像这样使用reducer
onActionHappen = function (action: HybridAction) {
const nextHistory = reducer(getLastHistory(), action)
updateAppHistory(nextHistory)
updateState(nextHistory.state)
}
内容版权声明:除非注明,否则皆为本站原创文章。
