撤销历史
编辑过程的一半是犯了小错误,并再次纠正它们。 因此,绘图程序中的一个非常重要的功能是撤消历史。
为了能够撤销更改,我们需要存储以前版本的图片。 由于这是一个不可变的值,这很容易。 但它确实需要应用状态中的额外字段。
我们将添加done
数组来保留图片的以前版本。 维护这个属性需要更复杂的状态更新函数,它将图片添加到数组中。
但我们不希望存储每一个更改,而是一定时间量之后的更改。 为此,我们需要第二个属性doneAt
,跟踪我们上次在历史中存储图片的时间。
function historyUpdateState(state, action) {
if (action.undo == true) {
if (state.done.length == 0) return state;
return Object.assign({}, state, {
picture: state.done[0],
done: state.done.slice(1),
doneAt: 0
});
} else if (action.picture &&
state.doneAt < Date.now() - 1000) {
return Object.assign({}, state, action, {
done: [state.picture, ...state.done],
doneAt: Date.now()
});
} else {
return Object.assign({}, state, action);
}
}
当动作是撤消动作时,该函数将从历史中获取最近的图片,并生成当前图片。
或者,如果动作包含新图片,并且上次存储东西的时间超过了一秒(1000 毫秒),会更新done
和doneAt
属性来存储上一张图片。
撤消按钮组件不会做太多事情。 它在点击时分派撤消操作,并在没有任何可以撤销的东西时禁用自身。
class UndoButton {
constructor(state, {dispatch}) {
this.dom = elt("button", {
onclick: () => dispatch({undo: true}),
disabled: state.done.length == 0
}, "⮪ Undo");
}
setState(state) {
this.dom.disabled = state.done.length == 0;
}
}