2021铁人赛
React
一般的内容网站通常都会有将内容分页或是动态读取的功能,例如像是脸书的动态页面,因为会不断地有新的动态,也就是说它的资料量是会快速增加的,就很适合使用动态读取(例如:scroll down)的功能;而像是博客来线上书店,新书发布的频率就没有脸书动态这麽高,因此系统可以在使用者进入网站的时候知道有多少资料需要呈现,就比较适合用分页来做,因为大概可以算出有几页。
当然,上面说得只是一种分辨的标准,来判断要使用动态读取或是分页功能,实际上除了资料量在一段时间内是否固定,还有很多其他面向需要考量,以这次的投资dashboard来说,因为我想要让一页最多就只有3张图表,而我也能在使用者一进入网站,系统就知道需要多少页面,因此我觉得分页的方式蛮适合的。
下图表示资料的传递方向,原本没有分页功能元件,资料直接从Charts进入Card元件渲染出来,有了Pagination分页元件後,就把它插在Charts与Card中间,做了分页处理之後,才会显示出来。
由於我希望让分页元件是比较灵活的,因此蒐集了网路上各种分页元件的写法,综合一版是我觉得功能比较符合我使用的,功能包含:可以设定每页的资料数量、可以设定要显示的分页数、可以显示目前是在哪一页、可以有上一页跟下一页的功能。程序码如下:
src\components\Selector\Selector.js
import React, { useState } from 'react';
import { Row } from 'react-bootstrap';
import styles from './pagination.module.css';
const Pagination = (props) => {
// 从props导入的资料,包含每页资料、要套入渲染的元件、显示分页数、每页资料数
const { data, RenderComponent, pageLimit, dataLimit } = props;
// 总分页数目
const pages = Math.ceil(data.length / dataLimit);
// 用一个state储存目前在哪个页面
const [currentPage, setCurrentPage] = useState(1);
// 下一页
const goToNextPage = () => {
setCurrentPage((page) => page + 1);
}
// 上一页
const goToPreviousPage = () => {
setCurrentPage((page) => page - 1);
}
// 跳至该页面
const changePage = (event) => {
const pageNumber = Number(event.target.textContent);
setCurrentPage(pageNumber);
}
// 载入当页资料
const getPaginatedData = () => {
const startIndex = currentPage * dataLimit - dataLimit;
const endIndex = startIndex + dataLimit;
return data.slice(startIndex, endIndex);
};
// 计算目前分页的数字是哪几个分页
const getPaginationGroup = () => {
let start = Math.floor((currentPage - 1) / pageLimit) * pageLimit;
return new Array(pageLimit).fill().map((_, idx) => start + idx + 1);
};
return (
<div>
<div className="dataContainer">
<Row>
{getPaginatedData().map((d, idx) => (
<RenderComponent key={idx} data={d} />
))}
</Row>
</div>
<div className={styles.pagination}>
<button
onClick={goToPreviousPage}
className={`${styles.prev} ${currentPage === 1 ? styles.disabled : ''}`}
>
prev
</button>
{getPaginationGroup().map((item, index) => (
<button
key={index}
onClick={changePage}
className={`${styles.paginationItem} ${currentPage === item ? styles.active : null}`}
>
<span>{item}</span>
</button>
))}
<button
onClick={goToNextPage}
className={`${styles.next} ${currentPage === pages || pages === 0 ? styles.disabled : ''}`}
>
next
</button>
</div>
</div>
);
};
export default Pagination;
CSS设定
src\UI\Pagination.module.css
.pagination {
display: flex;
align-items: center;
justify-content: center;
}
.paginationItem {
background: #fff;
border: 2px solid #666;
padding: 10px 15px;
border-radius: 50%;
height: 45px;
width: 45px;
position: relative;
margin: 0 5px;
cursor: pointer;
}
.paginationItem span {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.prev,
.next {
background: #fff;
border: none;
padding: 10px;
color: blue;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.4);
margin: 0 10px;
cursor: pointer;
}
.paginationItem.active {
border: 1px solid #888;
color: #888;
pointer-events: none;
}
.prev.disabled,
.next.disabled {
pointer-events: none;
box-shadow: none;
color: #999;
}
在Charts元件中插入分页元件并设定参数,这边稍微解释一下参数代表的意义:
src\components\Charts\Charts.js
import React from 'react';
import Card from './Card';
import Pagination from '../../UI/Pagination';
const Charts = (props) => {
return (
<Pagination
data={props.charts}
RenderComponent={Card}
pageLimit={Math.ceil(props.charts.length / 3)}
dataLimit={3}
/>
)
};
export default Charts;
做到这边会发现无法正常运作,因为Pagination这个元件给子元件的名称不符合之前的设定:
撷取一小段Paginaiton.js
<div className="dataContainer">
<Row>
{getPaginatedData().map((d, idx) => (
<RenderComponent key={idx} data={d} />
))}
</Row>
</div>
可以发现这边是用data,所以子元件就要用props.data,因为这次用的子元件是Card,就把它拿出来看看,发现原本是写props.item,只要把它都改成props.data即可:
src\components\Charts\Card.js
import Highcharts from 'highcharts/highstock';
import HighchartsReact from 'highcharts-react-official';
import fredAPI from './fredAPI';
+import { Col } from 'react-bootstrap';
const Card = (props) => {
const [chartOption, setChartOption] = useState({
title: {
- text: props.item.title
+ text: props.data.title
},
xAxis: {
type: "datetime",
...
},
series: [
{
- name: props.item.title
+ name: props.data.title
}
]
});
...
useEffect(() => {
- fetchData(props.item.series_id);
+ fetchData(props.data.series_id);
}, [fetchData, props]);
return (
- <div className={styles.chartFrame}>
- <HighchartsReact
- highcharts={Highcharts}
- constructorType={'stockChart'}
- options={chartOption}
- />
- <div className={styles.chartInfo}>
- <p className={styles.source}>source: {props.item.source}</p>
- <p className={styles.date}>updated: {props.item.updated}</p>
+ <Col sm={12} md={12} lg={6} xxl={4} className={styles.chartItem} key={props.data.id}>
+ <div className={styles.chartFrame}>
+ <HighchartsReact
+ highcharts={Highcharts}
+ constructorType={'stockChart'}
+ options={chartOption}
+ />
+ <div className={styles.chartInfo}>
+ <p className={styles.source}>source: {props.data.source}</p>
+ <p className={styles.date}>updated: {props.data.updated}</p>
+ </div>
+ <div>
+ <p className={styles.document}>{props.data.document}</p>
+ </div>
</div>
- <div>
- <p className={styles.document}>{props.item.document}</p>
- </div>
- </div>
+ </Col>
到这边大概就完成了,第一次写分页功能,还算是蛮快的,到目前就完成筛选跟分页功能,接下来要来解决网页在不同的分页来回,会重复打API的问题,详情见下篇。
<<: 依赖反转原则 Dependency Inversion Principle
>>: Day 13 - Spring Boot & JPA
用文本的方式打开文件,就可以看到版本了: FBXHeaderExtension: { FBXHead...
D12: break跟continue 在回圈里通常要执行完才能离开,这时候break的用意就是为了...
文章参考自 https://juejin.im/post/6844903744518389768 h...
画栏位线条时我们都要计算要画几条线以及每一格的大小,以便接下来完美的将物件填入格子里 像是: gri...
一、前言 会想了解 SSH 是因为工作上和自己使用 GitHub 时,都有看过这个名词,所以有稍...