首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

使用 Redux 实现时间旅行(1)

使用 Redux 实现时间旅行(1)

Redux 时间旅行回想一下,Redux 使用对象来表示状态,并使用纯函数计算下一个应用程序状态。这些特征使 Redux 成为了一个可预测        的状态容器,这意味着如果给定一个特定应用程序状态和一个特定操作,那么应用程序的下一个状态将始终完全相同。这种可预测性使得实现时间旅行变得很容易 —        能够在应用程序以前的状态中前后移动,并实时查看结果。图 2 演示了这一特性,其中给出了图书搜索应用程序的最终版本。
图 2. 状态历史和撤销/重做
在应用程序的最终版本中,页面顶部的控件组件包含一个历史滑块,和前移和后移时间的箭头。此外,页面底部有一个显示当前应用程序状态的状态查看器组件。
图 3 展示了如何通过操作应用程序的箭头和滑块,在应用程序以前的状态中移动。
图 3. 时间旅行
从图 3 中的顶部图片可以看到,搜索 Dr. Seuss 图书之前,我搜索了 border collie。从该图片和图 3 中中间的图片可以看到,我删除了          border collie 文本,键入了 seuss 并按 Enter        键,然后搜索开始。中间的图片显示了该搜索期间的应用程序。底部的图片显示了应用程序的最新状态,历史滑块图标始终位于右侧。
图 3 中的屏幕截图不太明显,但随着您拖动滑块的图标,可以看到文本 border collie 变成 border        colli,然后变成 border coll,随后变成 border        col,等等,直到该文本字段变为空的。然后,随着继续拖动滑块的图标,您会看到文本 s,然后是 su,然后是          sue,直到看到 suess。实际上,您可以向前和向后时间旅行,至少从您应用程序的角度讲是这样。
现在您已看到它的实际效果,我将介绍如何使用 Redux 实现时间旅行。
状态历史实现时间旅行相对比较简单。以下是我将要执行的操作。
首先,我将实现一个状态历史对象,该对象维护一个过去状态数组、一个未来状态数组和一个表示现在的状态。然后我将修改该应用程序,将它的所有状态都存储在状态历史对象中,并将历史滑块和箭头按钮连接到状态历史方法。
清单 1 显示了状态历史对象。
清单 1. 状态历史对象          (statehistory.js)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
export default {
  past: [],

  present: undefined,
  future: [],

  thereIsAPresent:     function() { return this.present != undefined; },
  thereIsAPast:        function() { return this.past.length > 0; },
  thereIsAFuture:      function() { return this.future.length > 0; },
  setPresent:          function(state) { this.present = state; },
  movePresentToPast:   function() { this.past.push(this.present); },
  movePresentToFuture: function() { this.future.push(this.present); },
  movePastToPresent:   function() { this.setPresent(this.past.pop()); },
  moveFutureToPresent: function() { this.setPresent(this.future.pop()); },

  push: function(currentState) {
    if(this.thereIsAPresent()) {
      this.movePresentToPast();
    }
    this.setPresent(currentState);
  },

  undo: function() {
    if(this.thereIsAPresent()) {
      this.movePresentToFuture(); // Moving back in time
      this.movePastToPresent();   // Moving back in time
    }
  },

  redo: function() {
    if(!this.thereIsAFuture()) { // No future!
      return;
    }

    if(this.thereIsAPresent()) {
      this.movePresentToPast(); // Moving forward in time
    }

    this.moveFutureToPresent(); // Moving forward in time
  },

  gotoState: function(i) {
    const index = Number(i);
    const allstates = [...this.past, this.present, ...this.future];

    this.present = allstates[index]
    this.past = allstates.slice(0, index)
    this.future = allstates.slice(index+1, allstates.length)
  }
}




状态历史对象是一个包含以下 4 个方法的状态堆栈:
  • push(state)
  • undo()
  • redo()
  • gotoState(stateIndex)
只要图书搜索应用程序分派一个操作,它就会调用状态历史对象的 push() 方法将当前状态推送到状态历史对象的内部堆栈上。
撤销/重做对于控件组件中的撤销/重做箭头,该应用程序需要状态历史对象的 undo() 和 redo()        方法。对于历史滑块,我需要能够跳到状态历史中的任何状态,所以状态历史对象提供了一个 gotoState() 方法。
StateHistory.push() 方法将当前状态设置为现在的状态。如果开始有一个现在的状态,该方法会将它移到过去。
如果没有现在的状态,undo() 方法什么都不会做。否则,它将现在的状态移到未来,随后将过去的状态移到现在。
如果没有未来状态,redo() 方法什么也不会做;否则,它将现在的状态移到过去,随后将未来的状态移到现在。
最后,gotoState() 方法直接进入与传递给函数的索引对应的状态。gotoState() 方法使用 JavaScript        展开运算符将所有状态存储在一个数组中,然后依据索引将该数组拆分为过去、现在和未来。
介绍了状态历史对象的实现后,我将返回到图书搜索应用程序的实现上。
返回列表