撤销历史

编辑过程的一半是犯了小错误,并再次纠正它们。 因此,绘图程序中的一个非常重要的功能是撤消历史。

为了能够撤销更改,我们需要存储以前版本的图片。 由于这是一个不可变的值,这很容易。 但它确实需要应用状态中的额外字段。

我们将添加done数组来保留图片的以前版本。 维护这个属性需要更复杂的状态更新函数,它将图片添加到数组中。

但我们不希望存储每一个更改,而是一定时间量之后的更改。 为此,我们需要第二个属性doneAt,跟踪我们上次在历史中存储图片的时间。

  1. function historyUpdateState(state, action) {
  2. if (action.undo == true) {
  3. if (state.done.length == 0) return state;
  4. return Object.assign({}, state, {
  5. picture: state.done[0],
  6. done: state.done.slice(1),
  7. doneAt: 0
  8. });
  9. } else if (action.picture &&
  10. state.doneAt < Date.now() - 1000) {
  11. return Object.assign({}, state, action, {
  12. done: [state.picture, ...state.done],
  13. doneAt: Date.now()
  14. });
  15. } else {
  16. return Object.assign({}, state, action);
  17. }
  18. }

当动作是撤消动作时,该函数将从历史中获取最近的图片,并生成当前图片。

或者,如果动作包含新图片,并且上次存储东西的时间超过了一秒(1000 毫秒),会更新donedoneAt属性来存储上一张图片。

撤消按钮组件不会做太多事情。 它在点击时分派撤消操作,并在没有任何可以撤销的东西时禁用自身。

  1. class UndoButton {
  2. constructor(state, {dispatch}) {
  3. this.dom = elt("button", {
  4. onclick: () => dispatch({undo: true}),
  5. disabled: state.done.length == 0
  6. }, "⮪ Undo");
  7. }
  8. setState(state) {
  9. this.dom.disabled = state.done.length == 0;
  10. }
  11. }