目次
作成するテーブル
- API実行時のレスポンスデータから取得した数値を表示したい
- 1:00〜24:00までの1時間ごとの数値を表示したい
- 1:00〜12:00、13:00〜24:00の2段に分けたい
- 「使用量A」と「使用量B」の2つのテーブルを作成したい
完成イメージ
APIのレスポンスデータは
{
"id": 00046,
"date": "2015-08-21",
"area": 1,
"amountUsedA01": 123,
"amountUsedA02": 345.45,
"amountUsedA03": 13.45,
・・・
"amountUsedA24": 123.45,
"amountUsedB01": 12.45,
"amountUsedB02": 3.45,
"amountUsedB03": 123.45,
・・・
"amountUsedB24": 123.45
}
のように返却されるとします。
元の構造(マークアップ)
<h2 className="c-heading02">使用量A</h2>
<div className="c-table__wrap">
<table className="c-table__contents">
<tbody>
<tr>
<th>1:00</th>
<th>2:00</th>
<th>3:00</th>
<th>4:00</th>
<th>5:00</th>
<th>6:00</th>
<th>7:00</th>
<th>8:00</th>
<th>9:00</th>
<th>10:00</th>
<th>11:00</th>
<th>12:00</th>
</tr>
<tr>
<td>1.12</td>
<td>14.2913</td>
<td>7.36</td>
<td>10.44</td>
<td>14.2913</td>
<td>9.66</td>
<td>4.66</td>
<td>14.2913</td>
<td>4.6</td>
<td>14.2913</td>
<td>14.2913</td>
<td>3.45</td>
</tr>
<tr>
<th>13:00</th>
<th>14:00</th>
<th>15:00</th>
<th>16:00</th>
<th>17:00</th>
<th>18:00</th>
<th>19:00</th>
<th>20:00</th>
<th>21:00</th>
<th>22:00</th>
<th>23:00</th>
<th>24:00</th>
</tr>
<tr>
<td>1.12</td>
<td>14.2913</td>
<td>7.36</td>
<td>10.44</td>
<td>14.2913</td>
<td>9.66</td>
<td>4.66</td>
<td>14.2913</td>
<td>4.6</td>
<td>14.2913</td>
<td>14.2913</td>
<td>3.45</td>
</tr>
</tbody>
</table>
</div>
<h2 className="c-heading02">使用量B</h2>
<div className="c-table__wrap">
<table className="c-table__contents">
<tbody>
<tr>
<th>1:00</th>
<th>2:00</th>
<th>3:00</th>
<th>4:00</th>
<th>5:00</th>
<th>6:00</th>
<th>7:00</th>
<th>8:00</th>
<th>9:00</th>
<th>10:00</th>
<th>11:00</th>
<th>12:00</th>
</tr>
<tr>
<td>0.0001</td>
<td>0</td>
<td>0.0001</td>
<td>14.2913</td>
<td>0.0219</td>
<td>0.0001</td>
<td>0</td>
<td>14.2913</td>
<td>0.0001</td>
<td>14.2913</td>
<td>0.0219</td>
<td>0.0219</td>
</tr>
<tr>
<th>13:00</th>
<th>14:00</th>
<th>15:00</th>
<th>16:00</th>
<th>17:00</th>
<th>18:00</th>
<th>19:00</th>
<th>20:00</th>
<th>21:00</th>
<th>22:00</th>
<th>23:00</th>
<th>24:00</th>
</tr>
<tr>
<td>0.0001</td>
<td>0</td>
<td>0.0001</td>
<td>14.2913</td>
<td>0.0219</td>
<td>0.0001</td>
<td>0</td>
<td>14.2913</td>
<td>0.0001</td>
<td>14.2913</td>
<td>0.0219</td>
<td>0.0219</td>
</tr>
</tbody>
</table>
</div>
<tableタグ内の構造>
1段目のtr:1:00〜12:00の時間帯
2段目のtr:1:00〜12:00の数値
3段目のtr:13:00〜24:00の時間帯
4段目のtr:13:00〜24:00の数値
となっています。
このままでは重複している箇所が多く冗長です。
簡易なコードにしたいと思います。
修正後(React + TypeScript)
コンポーネント
仮のパス:src/features/amount-used/components/AmountUsedTable.tsx
import { AmountUsedApiResponse } from '@/core/api';
type Props = {
data: AmountUsedApiResponse;
itemName: string;
};
export const AmountUsedTable = ({ data, itemName }: Props) => {
const generateHours = (from: number, to: number) =>
Array.from({ length: to - from + 1 }, (_, i) => from + i);
// 1段目のtr(1:00〜12:00)
const firstHalfHours = generateHours(1, 12);
// 3段目のtr(1:00〜12:00)
const secondHalfHours = generateHours(13, 24);
// 時間の行を作成
const hourRow = (hours: number[]) => (
<tr>
{hours.map((h) => (
<th key={h}>{h}:00</th>
))}
</tr>
);
// 数値の行を作成
const dataRow = (hours: number[]) => (
<tr>
{hours.map((h) => {
const key = `${itemName}${String(h).padStart(2, '0')}` as keyof AmountUsedApiResponse;
return <td key={h}>{data[key]}</td>;
})}
</tr>
);
return (
<table className="c-table__contents">
<tbody>
{hourRow(firstHalfHours)}
{dataRow(firstHalfHours)}
{hourRow(secondHalfHours)}
{dataRow(secondHalfHours)}
</tbody>
</table>
);
};
コンポーネントを呼び出す
仮のパス:src/features/amount-used/AmountUsedPage.tsx
(画面表示の役割を持つファイル)
<h2 className="c-heading02">使用量A</h2>
<div className="c-table__wrap">
<AmountUsedTable data={data} itemName="amountUsedA" />
</div>
<h2 className="c-heading02">使用量B</h2>
<div className="c-table__wrap">
<AmountUsedTable data={data} itemName="amountUsedB" />
</div>
解説
Array.from()
- 配列を生成するためのメソッド。
- 反復可能オブジェクトや配列風オブジェクトからシャローコピーされた、新しいArrayインスタンスを生成する。(Array.from() | MDN より)
- 第一引数:配列の長さを決める「配列風オブジェクト」
- 第二引数:map関数(配列の各要素をどう生成するか)
const generateHours = (from: number, to: number) =>
Array.from({ length: to - from + 1 }, (_, i) => from + i);
△「連続する時間の配列」を作成。
generateHours(1, 12)の場合、
const generateHours = (from: 1, to: 12) =>
Array.from({ length: 12 - 1 + 1 }, (_, i) => 1 + i);
となる。
(_, i) => from + i
- _ は配列の要素(今回は使わないので _ にしている)
- i は インデックス番号(0から始まる)
- from + iでは
i=0 → from + 0 = 1
i=1 → from + 1 = 2
...
i=11 → from + 11 = 12
となる。
配列の長さは12(12 個の要素を生成)となる。
fromからtoまでの連続する整数の配列を作成している。
Array.prototype.map()
- Arrayインスタンスのメソッド。
- 与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列を生成します。(Array.prototype.map() | MDN より)
const dataRow = (hours: number[]) => (
<tr>
{hours.map((h) => {
const key = `${itemName}${String(h).padStart(2, '0')}` as keyof AmountUsedApiResponse;
return <td key={h}>{data[key]}</td>;
})}
</tr>
);
- mapを使い、配列hoursの各時間(h)に対して処理する。
- 各時間に対応するtdタグから成るセルを作成する。
String.prototype.padStart()
Table作成自体に関係はありませんが、紹介します。
- ゼロ埋めを行う( 例:1月 → 01月 )
- String 値のメソッド
- 指定された文字列でこの文字列をパディングし(必要に応じて繰り返したり切り捨てたりして)、結果の文字列が指定された長さを示すようにする。
- パディングは、この文字列の先頭から適用される。(String.prototype.padStart() | MDN より)
padStart(2, '0')
- 第一引数:指定したい文字列の長さ。
- 第二引数:第一引数で指定した長さになるまで繰り返す文字列。省略できる。
ここではゼロ埋めしたいので「0」を指定し2桁の数値にしている。
keyの作成
const key =
`${itemName}${String(h).padStart(2, '0')}` as keyof AmountUsedApiResponse;
- itemName:ここでは「amountUsedA」か「amountUsedB」のいずれかが入る。
- amountUsedA01, amountUsedA02, amountUsedA03・・・amountUsedA24ができる。
データを表示
<td key={h}>{data[key]}</td>
- key={h}はReactのリストのキー(map内で必須)。
- {data[key]}はdataオブジェクトの該当する値を表示する。
例: data["amountUsedA01"] = 123
引用・参考サイト
