完成 React 版本从前面几期,已经了解了控件和状态查看器组件的实现,如图 1 所示。
图 1. 图书搜索应用程序
控件显示在应用程序顶部,左侧是 Topic 文本字段,右侧是箭头按钮。状态查看器组件在页面底部显示当前应用程序状态。现在我将解释 Books 组件,它将图书显示为文本或缩略图链接。
回想一下 “ ”,将 React 组件连接到 Redux 存储的一种方式是将它们实现为两个组件:一个容器包含另一个组件。容器连接到 Redux 存储,在状态更改时将属性导出到包含的组件中。包含的组件创建这些属性的可视表示。Books 组件也不例外。清单 1 给出了 Books 容器的代码。
清单 1. books 容器 (containers/book.js)1
2
3
4
5
6
7
8
9
10
11
12
13
| import { connect } from 'react-redux';
import Books from '../components/books';
const mapStateToProps = state => ({
books: state.books,
currentStatus: state.currentStatus,
displayMode: state.displayMode,
});
export default connect(
mapStateToProps,
null
)(Books);
|
Books 容器将状态映射到所包含的 Books 组件的属性。Books 的用途只是提供它们的存在状态,所以没有要指派的操作,这就解释了 connect() 函数的 null 参数。
清单 2 给出了 Books 容器中包含的 Books 组件的代码。
清单 2. books 组件 (components/books.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
65
66
67
68
69
70
71
72
73
74
75
76
| import React from 'react';
import Book from './book.js';
let ReactCSSTransitionGroup =
require('react-addons-css-transition-group');
const Books = ({
books,
displayMode,
currentStatus,
}) => {
const styles = {
container: {
width: '100%',
},
spinner: {
textAlign: 'center',
width: '100%',
},
};
const Spinner = () => (
<div style={styles.spinner}>
<img src="./images/spinner.gif"
role="presentation" />
</div>
);
const bookMarkup = () => {
let components = null;
let bookItems = (<span>No items!</span>);
if (books.length > 0) {
components = books.map(item => {
if (item.volumeInfo.imageLinks) {
// Need different keys for different display modes
// to trigger <ReactCSSTransitionGroup> animations
const key = displayMode === 'THUMBNAIL' ?
item.id + 1 :
item.id;
bookItems = (
<Book item={item}
displayMode={displayMode}
key={key} />);
}
return bookItems;
});
}
return components;
}
return (
<div>
{ currentStatus !== 'Fetching...' ? null : <Spinner /> }
<div style={styles.container}>
<ReactCSSTransitionGroup transitionName="books"
transitionLeaveTimeout={1}
transitionEnterTimeout={1000}>
{bookMarkup()}
</ReactCSSTransitionGroup>
</div>
</div>
);
};
Books.propTypes = {
books: React.PropTypes.array.isRequired,
currentStatus: React.PropTypes.string.isRequired,
displayMode: React.PropTypes.string.isRequired,
};
export default Books;
|
在应用程序抓取下一批图书时,Books 组件会显示一个下拉列表部件;否则该组件会显示存储在应用程序的状态中的图书。Books 组件使用 ReactCSSTransitionGroup 组件让显示的图书淡出。
从 Redux 角度讲,清单 2 中最重要的部分是传递给 Books 组件的 3 个属性:图书列表、显示模式和当前抓取状态。这些属性之所以可用,离不开 中对 connect() 函数的调用。回想一下,connect() 函数未包含在 Redux 中,而是由 react-redux 绑定提供。
在 中,bookMarkup 函数使用 Book 元素创建标记。Book 组件的代码如清单 3 所示。
清单 3. book 组件 (components/book.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
| import React from 'react';
const Book = ({
item,
displayMode,
}) => {
const styles = {
links: {
marginTop: '20px',
},
link: {
padding: '25px',
},
image: {
boxShadow: '3px 3px 3px #686868',
marginBottom: '15px',
},
};
const linkMarkup = (currentItem, link) => (
<div style={styles.links}>
<a href={link} style={styles.link}>
{currentItem.volumeInfo.title}
</a>
</div>
);
const thumbnailMarkup = (currentItem, link) => (
<a href={link} style={styles.link}>
<img src={currentItem.volumeInfo.imageLinks.thumbnail}
style={styles.image}
role="presentation" />
</a>
);
const link = item.volumeInfo.canonicalVolumeLink;
return displayMode === 'THUMBNAIL' ?
thumbnailMarkup(item, link) :
linkMarkup (item, link);
};
Book.propTypes = {
item: React.PropTypes.object.isRequired,
displayMode: React.PropTypes.string.isRequired,
};
export default Book;
|
Book 组件将图书显示为链接或缩略图。item 和 displayMode 属性来自 Books 组件,如 所示。
此组件完善了使用 React 和 Redux 的图书搜索应用程序的实现。React 不是您用于 Redux 的组件框架的唯一选择。接下来,将会了解如何结合使用 Redux 和 Angular 创建同一个应用程序。(如果仅使用 React,可以跳到 部分。) |