概要
前回の記事ではフロントエンド開発について書き切ることができませんでしたので今回はその続きの部分の実装について書いていければと思います。試合結果詳細ページ、試合結果の削除機能です。
試合結果詳細ページ
まずは試合結果詳細ページを実装する前に、試合結果一覧ページに詳細ページへ飛ぶボタンを設置したいと思います。ついでに編集、削除のボタンも設置しておきます。
<ActionButton color="blue">
<Link href={`/games/detail/${game.id}`}>
詳細
</Link>
</ActionButton>
<ActionButton color="green">
<Link href={`/games/edit/${game.id}`}>
編集
</Link>
</ActionButton>
<ActionButton
color="red"
onClick={() => handleDelete(game.id)}
>
削除
</ActionButton>
Nextのリンクコンポーネントを利用しつつ、idに紐づいた試合のページへ遷移するようにしています。それでは詳細ページの実装をしていきます。
ディレクトリ構成に関してですが、games/detail/[id]/page.tsxというようになっていてidによって表示するページが動的に変わるようにしています。以下return文までの実装になります。
export default function GameDetailPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id: gameId } = use(params);
const [game, setGame] = useState<Game | null>(null);
useEffect(() => {
const fetchGame = async () => {
try {
const response = await api.get<Game>(
`/games/${gameId}`
);
setGame(response.data);
} catch (error) {
console.error(
'Error fetching game details:',
error
);
}
};
fetchGame();
}, [gameId]);
if (!game) return <p>Loading...</p>
paramsはページのルートパラメータになっています。(/games/123の場合idが123になる)
次の、const { id: gameId } = use(params)では、paramsからidを取得し、gameIdに代入しています。
参考
https://zenn.dev/web_life_ch/articles/dcec0059a751b0
また、その下のuseEffectではコンポーネントの初回表示時と、gameIdが変更された時にAPIから試合の詳細データを取得してくるようになっています。
以下はreturn内の実装になります。
return (
<Container>
<Title>試合結果詳細</Title>
<DetailTable>
<tbody>
<tr>
<Td>Date</Td>
<Td>
{new Date(game.date).toLocaleDateString()}
</Td>
</tr>
<tr>
<Td>Home Team</Td>
<Td>{game.home_team}</Td>
</tr>
<tr>
<Td>Away Team</Td>
<Td>{game.away_team}</Td>
</tr>
</tbody>
</DetailTable>
<Scoreboard>
<thead>
<tr>
<th>Inning</th>
{[...Array(9)].map((_, i) => (
<th key={i}>{i + 1}</th>
))}
<th>R</th>
<th>H</th>
<th>E</th>
</tr>
</thead>
<tbody>
<tr>
<Td>Away</Td>
{[...Array(9)].map((_, i) => (
<Td key={i}>
{game[`away_inning_${i + 1}` as keyof Game]}
</Td>
))}
<Td>{game.away_total_score}</Td>
<Td>{game.away_total_hits}</Td>
<Td>{game.away_total_errors}</Td>
</tr>
<tr>
<Td>Home</Td>
{[...Array(9)].map((_, i) => (
<Td key={i}>
{game[`home_inning_${i + 1}` as keyof Game]}
</Td>
))}
<Td>{game.home_total_score}</Td>
<Td>{game.home_total_hits}</Td>
<Td>{game.home_total_errors}</Td>
</tr>
</tbody>
</Scoreboard>
<ButtonContainer>
<BackLink href="/games">一覧へ戻る</BackLink>
</ButtonContainer>
</Container>
);
}
削除機能の実装
既に試合結果の削除を行うActionButtonを設置しており、そのonClick時の処理としてhandleDeleteが呼び出されるようになっているので、handleDeleteの処理を定義していきたいと思います。
const handleDelete = async (id: number) => {
try {
await api.delete(`/games/${id.toString()}`);
setGames((prevGames) =>
prevGames.filter((game) => game.id !== id)
);
} catch (error) {
console.error('Error deleting game:', error);
}
};
全体の流れとしては、指定されたidを受け取り、その試合データをデータベースから削除しクライアント側からも該当の試合を削除するというようになっています。
まずは非同期関数として宣言し、削除対象となる試合の一意の識別子(id)を引数として受け取っています。api.deleteの部分はAxiosライブラリを利用して、DELETEメソッドでAPIにリクエストを送信しています。id.toStringの部分ではidが数値型で渡されるため、URL文字列に適合させるために文字列に変換しています。setGamesはuseStateで管理している試合データを更新する関数で、prevGames(現在の試合のリスト)をfilterメソッドでフィルタリングし削除対象であるidと一致しない試合のみ保持するようにしています。
今回はここまでにしたいと思います。
次回に編集ページの実装について書いて最後になるかと思います。
参考