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

使用 Redux 实现时间旅行-2

使用 Redux 实现时间旅行-2

状态历史实现时间旅行相对比较简单。以下是我将要执行的操作。
首先,我将实现一个状态历史对象,该对象维护一个过去状态数组、一个未来状态数组和一个表示现在的状态。然后我将修改该应用程序,将它的所有状态都存储在状态历史对象中,并将历史滑块和箭头按钮连接到状态历史方法。
清单 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        展开运算符将所有状态存储在一个数组中,然后依据索引将该数组拆分为过去、现在和未来。
介绍了状态历史对象的实现后,我将返回到图书搜索应用程序的实现上。
包含历史和撤销/重做功能的图书搜索应用程序清单 2 显示了图书搜索应用程序的 App 组件的最终实现。
清单 2. 最终的图书搜索应用程序          (containers/app.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
import React from 'react';
import ControlsContainer from './controls';
import BooksContainer from './books';
import StateViewerContainer from './stateviewer';

const titleStyle = {
  fontFamily: 'tahoma',
  fontSize: '24px',
  textAlign: 'center'
}

const Title = () => (
  <div style={titleStyle}>
    Book Search
  </div>
);

export default () => (
  <div>
    <Title />
    <hr/>
    <ControlsContainer    />
    <BooksContainer       />
    <StateViewerContainer />
  </div>
)




App 组件包含 3 个组件:ControlsContainer、BooksContainer 和          StateViewerContainer。ControlsContainer          包含历史滑块和箭头按钮,StateViewerContainer 包含一个显示当前状态的无状态组件。我首先会介绍          StateViewerContainer。
状态查看器组件清单 3 显示了状态查看器容器:
清单 3. 状态查看器容器          (containers/stateviewer.js)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { connect } from 'react-redux';
import StateViewer from '../components/stateviewer';
import stateHistory from '../statehistory';

const mapStateToProps = state => {
  return {
    books:         state.books,
    topic:         state.topic,
    currentStatus: state.currentStatus,
    displayMode:   state.displayMode,
    history:       stateHistory
  }
}

export default connect(
  mapStateToProps,
  null
)(StateViewer);




stateviewer 容器将 5 个属性映射到它包含的无状态组件。状态查看器无状态组件在清单 4 中使用了这些属性。
清单 4. stateviewer 无状态组件        (components/stateviewer.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
51
52
53
54
55
56
57
58
59
60
import React from 'react';

const StateViewer = ({
  topic,
  books,
  currentStatus,
  displayMode,
  history
}) => {
  const styles = {
    container: {
      margin: '20px',
      width: '400px',
      fontFamily: 'tahoma'
    },

    title: {
      fontSize: '24px',
      marginTop: '25px'
    },

    state: {
      marginTop: '10px'
    },

    hr: {
      marginTop: '50px'
    }
  };

  return(
    <div style={styles.container}>
      <hr style={styles.hr}/>

      <div style={styles.title}>
        Application State
      </div>

      <div style={styles.state}>
        Topic: {topic}<br/>

        Display mode:      { displayMode }<br/>
        Current status:    { currentStatus }<br/>
        Books displayed:   { books.length }<br/>
        Actions processed: { history.past.length + history.future.length + 1 }<br/>
        Current action:    { history.past.length + 1 }
      </div>
    </div>
  );
}

StateViewer.propTypes = {
  books: React.PropTypes.array.isRequired,
  currentStatus: React.PropTypes.string.isRequired,
  displayMode: React.PropTypes.string.isRequired,
  history: React.PropTypes.object.isRequired,
  topic: React.PropTypes.string.isRequired,
};

export default StateViewer;




清单 4 中的组件很简单;它仅显示自己的属性,这些属性来自包含它的 StateViewerContainer,该容器从当前状态获得它们。
这就是在页面底部显示应用程序状态的状态查看器。接下来我返回到更有趣的控件组件。
返回列表