React + Highcharts で都道府県の人口をグラフで表示するアプリ
はじめに
初めて記事のようなものを書きます.よろしくお願いします.
今回は2020年のインターンシップ選考で私が開発した,都道府県ごとの人口を表示するアプリについて開発した手順を書いていきたいと思います.
至らない点があれば、遠慮なくご指摘いただければと思います。
Demo
県名にチェックを入れると,その県の人口推移が表示されます.
複数選択すると、まとめて表示されます。
環境
- macOS Catalina (v10.15.6)
- Node.js (v14.8.0)
使用技術
- React.FC
- Highcharts:https://www.highcharts.com/
API(都道府県人口データ)
- RESAS API:https://opendata.resas-portal.go.jp/
API Key取得
今回、都道府県人口データを使用したいので、RESAS APIにアクセスして、利用登録をします。
取得したAPI Keyは後の開発手順で使用します。
開発手順
プロジェクト作成
まずは,作業ディレクトリに移り,プロジェクトを作成しましょう.
--template typescript
オプションをつけるとTypeScript用のプロジェクトが作成できます.
npx create-react-app --template typescript react-highcharts
必要なライブラリをインストール
これからプロジェクトに移動して、スクリプトを書き換えていきますが、必要なライブラリをインストールします。
外部リソースを取得するためのライブラリであるaxiosをインストールします。
fetchを使う場合は、この必要はありません。axiosを使う方がいくらか書く量が減りますが、それほど違いはないでしょう。
yarn add axios
highchartsとReactで使うためのライブラリがあったのでそれも。
yarn add highcharts
yarn add highcharts-react-official
API Key
次にプロジェクト直下に.env.local
ファイルを作成し、前の手順で取得したAPI Keyを書き込んでおきます。
環境変数は一般的に開発環境であれば.env.local
を作成して書きます。
ただ、環境変数の命名にはルールがあり、頭に必ずREACT_APPと付ける必要があります。
REACT_APP_API_KEY=<前の手順で取得したAPI_KEY>
Styleについて
ReactでCSS Styleを設定するにはさまざまな手段がありますが、今回は、React.CSSPropertiesを使おうと思います。CSSファイルに書かないインラインスタイルで、スタイル定義をコンポーネント内で完結できるのが特徴だと思います。(個人的にはMaterial-UIのmakeStyleみたいな感じという曖昧な認識です)
import React from "react";
const Styles: { [key: string]: React.CSSProperties } = {
button: {
margin: "1rem",
},
};
// サンプルButtonのコンポーネント
const Button: React.FC = () => {
return <button style={Styles.button}>hoge</button>;
};
export default Button;
コーディング
App.tsx
と同じ階層にcomponents
フォルダを作成し,その中にMain.tsx
とCheckField.tsx
とGraph.tsx
の3つのファイルを作成します.
Main.tsx
が親でCheckField.tsx
とGraph.tsx
を呼び出します。
呼び出されるコンポーネントから書いていきます。
都道府県の名前が並び、チェックを入れられるコンポーネント
import React from "react";
type Props = {
prefectures:
| {
prefCode: number;
prefName: string;
}[];
onChange: (name: string, prefName: number, check: boolean) => void;
};
const Styles: { [key: string]: React.CSSProperties } = {
checkcardList: {
display: "flex",
flexWrap: "wrap",
padding: "10px",
justifyContent: "flex-start",
justifySelf: "auto",
},
text: { display: "contents", marginLeft: "1em", cursor: "pointer" },
checkcard: {
borderRadius: "24px",
border: "solid 2px",
textAlign: "center",
padding: "4px",
margin: "0.5rem",
},
};
// 都道府県一覧のチェックボックスを表示するコンポーネント
const CheckField: React.FC<Props> = ({ prefectures, onChange }) => {
return (
<>
<div style={Styles.checkcardList}>
{prefectures.map((prefecture) => (
<div style={Styles.checkcard} key={prefecture.prefName}>
<input
type="checkbox"
name="Prefecture name"
onChange={(event) =>
onChange(
prefecture.prefName,
prefecture.prefCode,
event.target.checked
)
}
id={"checkbox" + prefecture.prefCode}
/>
<label
style={Styles.text}
htmlFor={"checkbox" + prefecture.prefCode}
>
{prefecture.prefName.length === 3
? " " + prefecture.prefName
: prefecture.prefName}
</label>
</div>
))}
</div>
</>
);
};
export default CheckField;
チェックを入れた都道府県のグラフを表示するコンポーネント
import React from "react";
import Highcharts from "highcharts";
import HighchartsReact from "highcharts-react-official";
const Styles: { [key: string]: React.CSSProperties } = {
graph: {
padding: "12px",
},
};
type Props = {
populationdata: {
prefName: string;
data: { year: number; value: number }[];
}[];
};
// 選んだ都道府県の人口推移グラフを表示するコンポーネント
const Graph: React.FC<Props> = ({ populationdata }) => {
let series: Highcharts.SeriesOptionsType[] = [];
let categories = [];
for (let p of populationdata) {
let data = [];
for (let pd of p.data) {
data.push(pd.value);
categories.push(String(pd.year));
}
series.push({
type: "line",
name: p.prefName,
data: data,
});
}
const options: Highcharts.Options = {
title: {
text: "総人口推移",
},
xAxis: {
title: {
text: "年度",
},
categories: categories,
},
yAxis: {
title: {
text: "人口数",
},
},
// 都道府県を一つも選んでいない場合との分岐条件
series:
series.length === 0
? [{ type: "line", name: "都道府県名", data: [] }]
: series,
};
return (
<div style={Styles.graph}>
<HighchartsReact highcharts={Highcharts} options={options} />
</div>
);
};
export default Graph;
APIからデータを取得し、CheckFieldコンポーネントとGraphコンポーネントを呼び出す
import React, { useEffect, useState } from "react";
import CheckField from "./CheckField";
import Graph from "./Graph";
import axios from "axios";
const Styles: { [key: string]: React.CSSProperties } = {
graph: {
padding: "10px",
},
label: {
fontSize: "20px",
padding: "0.5rem 2rem",
borderLeft: "4px solid #000",
marginLeft: "10pt",
},
};
const Main: React.FC = () => {
const [prefectures, setPreFectures] = useState<{
message: null;
result: {
prefCode: number;
prefName: string;
}[];
} | null>(null);
const [prefPopulation, setPrefPopulation] = useState<
{ prefName: string; data: { year: number; value: number }[] }[]
>([]);
useEffect(() => {
// 都道府県一覧を取得する
axios
.get("https://opendata.resas-portal.go.jp/api/v1/prefectures", {
headers: { "X-API-KEY": process.env.REACT_APP_API_KEY },
})
.then((results) => {
setPreFectures(results.data);
})
.catch((error) => {});
}, []);
// チェックボックスをクリックした際の処理
const handleClickCheck = (
prefName: string,
prefCode: number,
check: boolean
) => {
let c_prefPopulation = prefPopulation.slice();
// チェックをつけた処理
if (check) {
if (
c_prefPopulation.findIndex((value) => value.prefName === prefName) !==
-1
)
return;
axios
.get(
"https://opendata.resas-portal.go.jp/api/v1/population/composition/perYear?prefCode=" +
String(prefCode),
{
headers: { "X-API-KEY": process.env.REACT_APP_API_KEY },
}
)
.then((results) => {
c_prefPopulation.push({
prefName: prefName,
data: results.data.result.data[0].data,
});
setPrefPopulation(c_prefPopulation);
})
.catch((error) => {
return;
});
}
// チェックを外した処理
else {
const deleteIndex = c_prefPopulation.findIndex(
(value) => value.prefName === prefName
);
if (deleteIndex === -1) return;
c_prefPopulation.splice(deleteIndex, 1);
setPrefPopulation(c_prefPopulation);
}
};
return (
<main>
<h2 style={Styles.label}>都道府県</h2>
{prefectures && (
<CheckField
prefectures={prefectures.result}
onChange={handleClickCheck}
/>
)}
<h2 style={Styles.label}>人口推移グラフ</h2>
<Graph populationdata={prefPopulation} />
</main>
);
};
export default Main;
App.tsx
最後に大元のApp.tsx
をこのように書き換えます。
処理は単純にMain.tsx
を呼び出しているだけです.
import React from "react";
import Main from "./components/Main";
const App: React.FC = () => {
return (
<div className="App">
<header style={{ textAlign: "center" }}>
<h1>都道府県別人口推移</h1>
</header>
<Main />
</div>
);
};
export default App;
終わりに
最近はNext.jsでスタイル設定はTailwindCSSを使っているので、すっかり忘れてしまっていました。
自分にとって、初めて外部APIを使ったReactの開発だったので、経験すると実現できる範囲が格段に広がるような思いがありました。
ここまでお読みいただきありがとうございました。何か力になれれば幸いです。
では。
リポジトリ
Discussion