Carousel
在本节,我们会使用第三方库React-Slick来构建“旋转木马组件”,并通过Ajax来获取数据源。
Mock API 地址:
使用React-Slick
首先,你需要用最简单的例子,让React-Slick可以正常的在页面显示 ./app/components/Carousel/index.jsx:
import React from 'react';
import Slider from 'react-slick';
import 'slick-carousel/slick/slick.css'
import 'slick-carousel/slick/slick-theme.css'
class Carousel extends React.Component {
render() {
var settings = {
dots: true,
infinite: true,
speed: 500,
slidesToShow: 1,
slidesToScroll: 1
};
return (
<Slider {...settings}>
<div><h3>1</h3></div>
<div><h3>2</h3></div>
<div><h3>3</h3></div>
<div><h3>4</h3></div>
<div><h3>5</h3></div>
<div><h3>6</h3></div>
</Slider>
);
}
}
export default Carousel;
PS:也许你会考虑像官方文档那样,在styles.scss文件引用slick.scss和slick-theme.scss,但由于node-sass对相对路径资源解析一直都存在问题,所以Webpack编译会失败。
@import "~slick-carousel/slick/slick.scss";
@import "~slick-carousel/slick/slick-theme.scss";
开始写代码
本次Carousel的分步任务有三点:1.Carousel中每一项的DOM结构和样式 2.复写React-Slick中箭头的样式 3.Ajax请求获取数据
Carousel单个Item
可以首先去完成Carousel中每个Item的样式,它的DOM结构比较简单:
<div className={cx('item')}>
<img src={item.image} />
<div className={cx('description')}>
<span className={cx('price')}>${item.price}</span>
{item.name}
</div>
<div className={cx('comments')}>
<span>{this.renderRankingStar(item.ranking)}</span>
<span className={cx('count')}>{item.commentsCount}则评价</span>
</div>
</div>
这里定义了一个函数renderStar,要根据rankingCount具体的值,来绘制评价的星级。(知识点:关于React中的箭头函数)
renderRankingStar = (ranking) => {
let j = 1;
const stars = [];
for (; j <= ranking; j++) {
stars.push(<i key={`ranting-${j}`} className={cx('star')} />);
}
return stars;
}
然后根据DOM完成样式(参考Airbnb):
.container {
max-width: 1020px;
margin: 50px auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-family: Circular, -apple-system, BlinkMacSystemFont, Roboto, Helvetica Neue, sans-serif;
.item {
width: 190px;
.description {
color: #484848;
font-weight: 300;
margin: 0px;
word-wrap: break-word;
font-size: 15px;
line-height: 18px;
letter-spacing: 0.2px;
max-height: 36px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
.price {
font-weight: 700;
color: #484848;
margin: 0px;
word-wrap: break-word;
font-size: 15px;
line-height: 18px;
letter-spacing: 0.2px;
padding-top: 0px;
padding-bottom: 0px;
padding-right: 1px;
display: inline;
}
}
.star {
background: url("./star.svg") no-repeat center center;
height: 12px;
width: 12px;
display: inline-block;
margin-right: 2px;
}
.comments {
margin-top: 5px;
.count {
font-weight: normal;
color: #484848;
font-size: 12px;
letter-spacing: 0.4px;
}
}
}
}
你在写的时候,可以先硬编码(hard coding)一些假数据,方便快速验证结果。
复写React-Slick的箭头样式
Airbnb的previewButton和nextButton与Slick的默认样式不一致,我们需要通过CSS来复写样式:
.container {
:global {
.slick-prev {
&:before {
content: " ";
background: url("./preview-arrow.svg") no-repeat center center;
height: 24px;
width: 24px;
display: inline-block;
left: -6px;
position: absolute;
}
}
.slick-next {
&:before {
content: " ";
background: url("./next-arrow.svg") no-repeat center center;
height: 24px;
width: 24px;
display: inline-block;
left: -8px;
position: absolute;
}
}
.slick-prev.slick-disabled:before, .slick-next.slick-disabled:before {
background-image: none;
}
}
}
这里要注意的是,需要添加一个:global的标签,CSS Modules在处理类名的时候,就会将它排除。(知识点:CSS Modules的Global和Local)。
数据请求
首先,需要添加你喜欢的Promise库,我使用ThoughtWorks技术雷达推荐的axios。
那么问题来了,在React中,应该在哪里编写获取数据的代码呢?(即什么时候去调用ajax)答案是:componentDidMount。
componentDidMount() {
axios.get(this.props.url)
.then((response) => {
this.setState({ items: response.data })
})
.catch(function (error) {
console.log(error);
});
}
完整代码如下:
import React from 'react';
import Slider from 'react-slick';
import classNames from 'classnames/bind';
import axios from 'axios';
import styles from './styles.scss';
import 'slick-carousel/slick/slick.css'
import 'slick-carousel/slick/slick-theme.css'
const cx = classNames.bind(styles);
class Carousel extends React.Component {
constructor(props) {
super(props);
this.state = {
items: []
};
}
componentDidMount() {
axios.get(this.props.url)
.then((response) => {
this.setState({ items: response.data })
})
.catch(function (error) {
console.log(error);
});
}
renderRankingStar = (ranking) => {
let j = 1;
const stars = [];
for (; j <= ranking; j++) {
stars.push(<i key={`ranting-${j}`} className={cx('star')} />);
}
return stars;
}
renderCarousel = (settings, items) => {
if (items.length === 0) return null;
return (
<Slider {...settings}>
{
items.map((item, index) => (
<div key={`carousel-item-${item.name}-${index}`}>
<div className={cx('item')}>
<img src={item.image} />
<div className={cx('description')}>
<span className={cx('price')}>${item.price}</span>
{item.name}
</div>
<div className={cx('comments')}>
<span>{this.renderRankingStar(item.ranking)}</span>
<span className={cx('count')}>{item.commentsCount}则评价</span>
</div>
</div>
</div>
))
}
</Slider>
);
}
render() {
var settings = {
infinite: false,
speed: 500,
slidesToShow: 5,
slidesToScroll: 1,
draggable: false
};
return (
<div className={cx('container', this.props.className)}>
<h3>{this.props.title}</h3>
{this.renderCarousel(settings, this.state.items)}
</div>
);
}
};
Carousel.propTypes = {
title: React.PropTypes.string
}
export default Carousel;
.container {
max-width: 1020px;
margin: 50px auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-family: Circular, -apple-system, BlinkMacSystemFont, Roboto, Helvetica Neue, sans-serif;
h3 {
font-size: 28px;
line-height: 36px;
letter-spacing: -0.6px;
padding-top: 2px;
padding-bottom: 2px;
color: #484848;
font-weight: 700;
}
.item {
width: 190px;
img {
max-width: 100%;
max-height: 100%;
}
.description {
color: #484848;
font-weight: 300;
margin: 0px;
word-wrap: break-word;
font-size: 15px;
line-height: 18px;
letter-spacing: 0.2px;
max-height: 36px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
.price {
font-weight: 700;
color: #484848;
margin: 0px;
word-wrap: break-word;
font-size: 15px;
line-height: 18px;
letter-spacing: 0.2px;
padding-top: 0px;
padding-bottom: 0px;
padding-right: 1px;
display: inline;
}
}
.star {
background: url("./star.svg") no-repeat center center;
height: 12px;
width: 12px;
display: inline-block;
margin-right: 2px;
}
.comments {
margin-top: 5px;
.count {
font-weight: normal;
color: #484848;
font-size: 12px;
letter-spacing: 0.4px;
}
}
}
:global {
.slick-prev {
&:before {
content: " ";
background: url("./preview-arrow.svg") no-repeat center center;
height: 24px;
width: 24px;
display: inline-block;
left: -6px;
position: absolute;
}
}
.slick-next {
&:before {
content: " ";
background: url("./next-arrow.svg") no-repeat center center;
height: 24px;
width: 24px;
display: inline-block;
left: -8px;
position: absolute;
}
}
.slick-prev.slick-disabled:before, .slick-next.slick-disabled:before {
background-image: none;
}
}
}
代码
你可以在prepare-env分支查看到当前步骤所有的代码
git clone [email protected]:ThoughtWorksWuhanUI/react-zero-to-one.git
git checkout carousel
本页编辑:Benwei