2021铁人赛
React
还记得这个网站有筛选图表的功能吗?当初画wireframe的时候考量到未来图表可能会越来越多,因此规划了下拉式选单来做筛选功能,如下图:
本篇就来用React写写看这个筛选功能吧!
先来看看"美国制造业电子零件订单"这个series,在FRED网站上是怎麽分类的,如下图:
可以看到下面有一些比较详细的资料,这边把它放大一下,里面包含几项内容:
这边稍微解释一下series、source、release三者的关系,series是指时间序列资料,例如"美国制造业电子零件订单"这个series是由过去每个月的数据组成,将这些数据依照时间排序就是一个序列,而一个series来自一个release,一个source通常有多个releases,一个release可能来自多个sources。
当要做筛选功能的时候,可以做的筛选方式可能就会有三种方式:
因此首先下拉式选单内要有可以选择的source与release,这些资料可以从FRED API取得,不过如果把API可以拿到的所有sources与releases放进来的话有点太多,因此我先依照目前有用到的series去列出有用到的sources与releases就好,制作成sources.json与releases.json如下:
src\data\sources.json
[
{
"id": 1,
"realtime_start": "2021-08-22",
"realtime_end": "2021-08-22",
"name": "Board of Governors of the Federal Reserve System (US)",
"link": "http://www.federalreserve.gov/"
},
{
"id": 19,
"realtime_start": "2021-08-22",
"realtime_end": "2021-08-22",
"name": "U.S. Census Bureau",
"link": "http://www.census.gov/"
}
]
src\data\releases.json
[
{
"id": 18,
"realtime_start": "2021-08-22",
"realtime_end": "2021-08-22",
"name": "H.15 Selected Interest Rates",
"press_release": true,
"link": "http://www.federalreserve.gov/releases/h15/"
},
{
"id": 20,
"realtime_start": "2021-08-22",
"realtime_end": "2021-08-22",
"name": "H.4.1 Factors Affecting Reserve Balances",
"press_release": true,
"link": "http://www.federalreserve.gov/releases/h41/"
},
{
"id": 95,
"realtime_start": "2021-08-22",
"realtime_end": "2021-08-22",
"name": "Manufacturer's Shipments, Inventories, and Orders (M3) Survey",
"press_release": true,
"link": "http://www.census.gov/indicator/www/m3/"
}
]
原本的series资料只有release_id去辨识,为了希望能用来源去筛选,因此要在series资料内新增source_id辨识来源:
src\data\chart-collections.json
[
{
"id": 1,
"series_id": "TREAST",
"release_id": 20,
// 新增source id
"source_id": 1,
...
},
{
"id": 2,
"series_id": "DGS10",
"release_id": 95,
// 新增source id
"source_id": 1,
...
},
{
"id": 3,
"series_id": "A34HNO",
"release_id": 18,
// 新增source id
"source_id": 19,
...
}
]
既然我们有了sources.json与releases.json,就可以把这些资料丢给React,这边有两个可以import资料的选择,一个是从最上层的App.js引入,另外一个是从选单元素Selector.js引入,考量到之後可能会在除了下拉式选单的地方用到这些资料,因此选择从App.js引入,再透过props传递到子元件。
从App引入资料,并透过props传递给Selector元件。
src\App.js
import Navbar from './components/Navbar/Navbar';
import Selector from './components/Selector/Selector';
import Charts from './components/Charts/Charts';
import chartCollections from './data/chart-collections.json';
// import 选单资料
import releases from './data/releases.json';
import sources from './data/sources.json';
function App() {
return (
<div className="App">
<Navbar />
<Selector
releases={releases}
sources={sources}
/>
<Charts charts={chartCollections} />
</div>
);
}
export default App;
在Selector元件接收props,并使用list render制作下拉式选单的option。
src\components\Selector\Selector.js
import React from 'react';
import styles from './Selector.module.css';
import Form from 'react-bootstrap/Form';
import { Row, Col } from 'react-bootstrap';
const Selector = (props) => {
return <Row className={styles.selector}>
<Col sm={12} md={6} lg={4} className={styles.selectorItem}>
<Form.Select
aria-label="Default select example"
>
<option value={0}>All Sources</option>
{props.sources.map((e) => (
<option value={e.id} key={e.id}>{e.name}</option>
))}
</Form.Select>
</Col>
<Col sm={12} md={6} lg={4} className={styles.selectorItem}>
<Form.Select
aria-label="Default select example"
>
<option value={0}>All Releases</option>
{props.releases.map((e) => (
<option value={e.id} key={e.id}>{e.name}</option>
))}
</Form.Select>
</Col>
</Row>
};
export default Selector;
这次的下拉式选单功能,想做到让使用者选取项目後,不需要再按一个submit的按钮,下方的图表就会自动筛选。为了要做到这个功能,首先要让React知道使用者选了什麽选项,如果选项有改变,React也要马上启动筛选机制。
因此,我想到了使用state去追踪下拉式选单的状态,也就是让React记住当前选到的source_id及release_id,当使用者操作下拉式选单,就会去调整对应的state,再根据调制後的state去筛选图表。
建立filteredReleaseId与filteredSourceId两个State,并且透过props传递给Selector元件。
src\App.js
...
function App() {
const [filteredReleaseId, setFilteredReleaseId] = useState(0);
const [filteredSourceId, setFilteredSourceId] = useState(0);
return (
<div className="App">
<Navbar />
<Selector
selectedReleaseId={filteredReleaseId}
selectedSourceId={filteredSourceId}
releases={releases}
sources={sources}
/>
<Charts charts={chartCollections} />
</div>
);
}
export default App;
在Form.Select设定defaultValue为props传递进来的资料。
src\components\Selector\Selector.js
...
const Selector = (props) => {
return <Row className={styles.selector}>
<Col sm={12} md={6} lg={4} className={styles.selectorItem}>
<Form.Select
aria-label="Default select example"
defaultValue={props.selectedSourceId}
>
<option value={0}>All Sources</option>
{props.sources.map((e) => (
<option value={e.id} key={e.id}>{e.name}</option>
))}
</Form.Select>
</Col>
<Col sm={12} md={6} lg={4} className={styles.selectorItem}>
<Form.Select
aria-label="Default select example"
defaultValue={props.selectedReleaseId}
>
<option value={0}>All Releases</option>
{props.releases.map((e) => (
<option value={e.id} key={e.id}>{e.name}</option>
))}
</Form.Select>
</Col>
</Row>
};
export default Selector;
可以透过filteredReleaseId与filteredSourceId去筛选图表资料,方式为:建立filteredCharts储存筛选後符合条件的资料,再将其传递到Charts元件。
src\App.js
...
function App() {
const [filteredReleaseId, setFilteredReleaseId] = useState(0);
const [filteredSourceId, setFilteredSourceId] = useState(0);
// 筛选图表的方式
const filteredCharts = chartCollections.filter(chart => {
if (filteredReleaseId === 0 && filteredSourceId === 0) return true
if (filteredReleaseId === 0 && filteredSourceId !== 0) {
return chart.source_id === filteredSourceId
}
if (filteredReleaseId !== 0 && filteredSourceId === 0) {
return chart.release_id === filteredReleaseId
}
if (filteredReleaseId !== 0 && filteredSourceId !== 0) {
return chart.release_id === filteredReleaseId && chart.source_id === filteredSourceId
}
});
return (
<div className="App">
<Navbar />
<Selector
selectedReleaseId={filteredReleaseId}
selectedSourceId={filteredSourceId}
releases={releases}
sources={sources}
/>
<Charts charts={filteredCharts} />
</div>
);
}
export default App;
上面的程序码知道如何筛选图表资料,接着,我们要让使用者透过下拉式选单去改变filteredReleaseId与filteredSourceId这两个state,就会促使React启动筛选的程序。
作法是建立两个setState函数,并将两个函数传递给Selector元件,让子元件可以呼叫函数修改父元件的state,这个过程称为资料的逆向传递。
src\App.js
...
function App() {
const [filteredReleaseId, setFilteredReleaseId] = useState(0);
const [filteredSourceId, setFilteredSourceId] = useState(0);
const filteredCharts = chartCollections.filter(chart => {
if (filteredReleaseId === 0 && filteredSourceId === 0) return true
if (filteredReleaseId === 0 && filteredSourceId !== 0) {
return chart.source_id === filteredSourceId
}
if (filteredReleaseId !== 0 && filteredSourceId === 0) {
return chart.release_id === filteredReleaseId
}
if (filteredReleaseId !== 0 && filteredSourceId !== 0) {
return chart.release_id === filteredReleaseId && chart.source_id === filteredSourceId
}
});
// setState函数
const releaseIdChangeHandler = (selectedReleaseId) => {
setFilteredReleaseId(selectedReleaseId);
};
// setState函数
const sourceIdChangeHandler = (selectedSourceId) => {
setFilteredSourceId(selectedSourceId);
};
return (
<div className="App">
<Navbar />
<Selector
selectedReleaseId={filteredReleaseId}
selectedSourceId={filteredSourceId}
releases={releases}
sources={sources}
onReleaseIdChange={releaseIdChangeHandler}
onSourceIdChange={sourceIdChangeHandler}
/>
<Charts charts={filteredCharts} />
</div>
);
}
export default App;
在Selector的Form.Select元件内新增onChange事件,再透过父元件传递进来的事件将筛选到的值逆向传递回去。这边要注意的是子元件不能直接修改父元件的state,但是可以透过props将资料逆向传递回去,透过父元件的setState函数去修改父元件的state。
src\components\Selector\Selector.js
...
const Selector = (props) => {
const releaseIdChange = (event) => {
props.onReleaseIdChange(Number(event.target.value));
};
const sourceIdChange = (event) => {
props.onSourceIdChange(Number(event.target.value));
};
return <Row className={styles.selector}>
<Col sm={12} md={6} lg={4} className={styles.selectorItem}>
<Form.Select
aria-label="Default select example"
defaultValue={props.selectedSourceId}
onChange={sourceIdChange}
>
<option value={0}>All Sources</option>
{props.sources.map((e) => (
<option value={e.id} key={e.id}>{e.name}</option>
))}
</Form.Select>
</Col>
<Col sm={12} md={6} lg={4} className={styles.selectorItem}>
<Form.Select
aria-label="Default select example"
defaultValue={props.selectedReleaseId}
onChange={releaseIdChange}
>
<option value={0}>All Releases</option>
{props.releases.map((e) => (
<option value={e.id} key={e.id}>{e.name}</option>
))}
</Form.Select>
</Col>
</Row>
};
export default Selector;
看似简单的筛选功能,其实也是蛮多程序码的,不过藉此也学到资料如何在父元件与子元件间传递,算是React内非常基本的知识。
虽然目前有了筛选功能,但是当图表越来越多张的时候,还是可能让画面变得很长,下一篇就来解决这个问题,最基本的方式应该就是用分页来处理。
要使用图来表达与非专业人员的执行程序与流程,除了一般的流程图就能做到之外,本题要介绍的这个图,是用角...
使用 KubeEye 为你的 K8s 集群安全保驾护航 其他 2022-04-24 18:51:30...
我们开始往地下三楼移动。 「你就真的不好奇,那个Blue的状况吗?」 『好奇,然後呢? 然後能有什然...
在 5 年前,不只在开会时会这样要求,也常常说一个概念:SEO 是一种 Multi-Stack 的工...
前言 昨天已经模拟出改价了,现在更进阶,使用小台的现价来改价。 参考网站:Futures 本日程序码...