状态控件在本系列的 中,我讨论了控件组件,它(像 Redux 应用程序中的许多组件一样)被拆分为一个容器组件和一个包含的无状态组件。容器组件将主题和显示模式映射到无状态组件。此外,该无状态组件(如清单 5 所示)包含 HistoryContainer 组件,所有时间旅行操作都在该组件中发生。
清单 5. 控件无状态组件 (components/controls.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
| import React from 'react';
import DisplayModeContainer from '../containers/displayMode';
import TopicSelectorContainer from '../containers/topicSelector';
import HistoryContainer from '../containers/history';
const Controls = ({
topic,
displayMode
}) => {
const styles = {
controls: {
padding: '15px',
marginBottom: '25px'
}
};
return(
<div style={styles.controls}>
<TopicSelectorContainer topic={topic} />
<DisplayModeContainer displayMode={displayMode} />
<HistoryContainer />
</div>
);
}
Controls.propTypes = {
displayMode: React.PropTypes.string.isRequired,
topic: React.PropTypes.string.isRequired,
};
export default Controls;
|
历史容器和无状态组件HistoryContainer 组件如清单 6 所示。
清单 6. 历史容器 (containers/history.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
| import { connect } from 'react-redux';
import { undo, redo, gotoState } from '../actions';
import { History } from '../components/history';
import stateHistory from '../statehistory';
const mapStateToProps = state => {
return {
past: stateHistory.past,
present: stateHistory.present,
future: stateHistory.future
}
}
const mapDispatchToProps = dispatch => {
return {
undo: () => {
dispatch(undo());
},
redo: () => {
dispatch(redo());
},
gotoState: stateIndex => {
dispatch(gotoState(stateIndex));
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(History);
|
历史容器将 past、present 和 future 作为属性映射到它包含的 History 组件。它还将 3 个函数(undo()、redo() 和 gotoState())作为属性映射到 History 组件。
请注意,将历史容器映射到其无状态组件的函数都会分派 Redux 操作。实现时间旅行涉及到更改状态,清单 6 中的函数通过分派 Redux 操作来实现此目的。操作是由操作创建器创建的,如清单 7 所示。
清单 7. 历史操作创建器 (actions.js)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| ...
export const redo = () => {
return {
type: 'REDO'
}
}
export const undo = () => {
return {
type: 'UNDO'
}
}
export const gotoState = stateIndex => {
return {
type: 'GOTO',
stateIndex
}
}
|
与操作创建器中的常见情况一样,清单 7 中的操作创建器很简单 — 它们仅返回操作对象。只有 gotoState 操作提供了状态更改类型以外的信息。该信息是历史对象所维护的状态数组的索引。
清单 8 显示了 history 无状态组件。
清单 8. history 无状态组件 (components/history.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
61
62
63
64
| import React from 'react';
export const History = ({
past, present, future,
undo, redo, gotoState,
}) => {
const styles = {
container: {
marginLeft: '20px',
cursor: 'pointer',
},
link: { textDecoration: 'none' },
input: { cursor: 'pointer' }
}
const RightArrow = () => {
return(
<a href='#' style={styles.link}
onClick={() => redo()}> →
</a>
);
}
const LeftArrow = () => {
return(
<a href='#' style={styles.link}
onClick={() => undo()}> ←
</a>
);
}
const maxRange = () => {
return (past ? past.length : 0) +
(present? 1 : 0) +
(future ? future.length : 0) - 1;
}
return(
<span style={styles.container}>
History
<input type='range'
style={styles.input}
min={0}
max={maxRange()}
value={past ? past.length : 0}
onChange={event => gotoState(event.target.value)}/>
{ (past && past.length > 0) ? <LeftArrow /> : null }
{ (future && future.length > 0) ? <RightArrow /> : null }
</span>
)
}
History.propTypes = {
past: React.PropTypes.array.isRequired,
present: React.PropTypes.object.isRequired,
future: React.PropTypes.array.isRequired,
undo: React.PropTypes.func.isRequired,
redo: React.PropTypes.func.isRequired,
gotoState: React.PropTypes.func.isRequired,
};
|
history 无状态组件显示历史滑块和箭头。当用户单击某个箭头时,此组件调用它以属性形式从包含它的容器组件收到的相应 undo() 或 redo() 函数。
当用户操作滑块时,历史无状态组件会调用 gotoState() 函数,该函数也是它以属性形式从包含它的容器中收到的。
目前为止,为了实现历史和撤销/重做功能,我实现了一个跟踪过去、现在和未来的状态历史对象。我介绍了 React 组件的实现,这些组件包含操作当前状态的控件和显示该状态的 HTML 元素。
这里保留了一个关键功能:处理 中定义的 undo、redo 和 goto 操作。接下来,我将介绍如何执行这些操作。 |