はじめに
職業プログラマであれば、一覧画面は避けては通れません。
管理画面があるシステムであれば、ほぼ間違いなく一覧画面を持っています。
難易度も比較的低いので、新人さんに回ってきやすい機能といえるでしょう。
かくいう私も、初めての実務ではJSPサーブレットで一覧画面を作りました。
まだ実装したことがないという方は、ぜひこのタイミングで一度作っておきましょう。
そうすれば実務で回ってきても安心です。
ちゃちゃっと実装して、上司に「お?もうできたの?」と言わせちゃいましょう。
実物はこちら
リポジトリはこちら
概要
この記事を読むことで、以下のような画面が作れるようになります。
以下の仕様を想定して作成しています。
- 従業員の一覧を表示する
- 画面の初期表示時は検索された状態
- 「名前」で絞り込みが可能
- 「名前」の入力欄でバリデーションエラーがあるときは検索ボタンは押せない
実装
先にコード全容です。
'use client'
import '../style.css'
import {Box, Button} from "@mui/material";
import {Input} from "@/components/input";
import {useTextInput} from "@/hooks/use-text-input";
import {tableUsers} from "@/data/data";
import {FetchTableUsersRequestType, TableUser} from "@/types/table";
import {useEffect, useState} from "react";
const fetchUsers = async (param: FetchTableUsersRequestType): Promise<Array<TableUser>> => {
if (process.env.NEXT_PUBLIC_USE_MOCK_DATA === 'true') {
return tableUsers.filter(user => user.name.indexOf(param.name ?? '') > -1)
} else {
const response = await fetch('/api/table/users', {
method: 'POST',
body: JSON.stringify(param)
})
const {users} = await response.json()
return users
}
}
export default function TableScratch() {
const [searchWord, setSearchWord] = useTextInput()
const [tableUserData, setTableUserData] = useState<Array<TableUser>>([])
useEffect(() => {
(async () => {
const newUsers = await fetchUsers({ name: null })
setTableUserData(newUsers)
})()
}, [])
const handleClick = async () => {
const newUsers = await fetchUsers({ name: searchWord.value })
setTableUserData(newUsers)
}
return (
<main>
<Box m={2}>
<Box display="flex">
<Box>
<Input label="名前" errors={searchWord.errors} onChange={(e) => setSearchWord(e.target.value)} />
</Box>
<Box ml={3} pt={3}>
<Button
type="button"
variant="contained"
disabled={searchWord.errors.length > 0}
onClick={handleClick}
>
検索
</Button>
</Box>
</Box>
</Box>
<Box m={2}>
<table>
<thead>
<tr>
<th>名前</th>
<th>所属</th>
<th>好きなもの</th>
</tr>
</thead>
<tbody>
{
tableUserData.map((user) => (
<tr key={user.id}>
<td>{user.name}</td>
<td>{user.department}</td>
<td>{user.favoriteThings}</td>
</tr>
))
}
</tbody>
</table>
</Box>
</main>
)
}
各部分の解説
一覧取得処理
一覧データの取得は fetchUsers
関数で行っています。
初期表示時にはすでに一覧が表示されているようにしたいので、useEffect
フックを使用して初期表示時に検索処理を実行しています。
const fetchUsers = async (param: FetchTableUsersRequestType): Promise<Array<TableUser>> => {
if (process.env.NEXT_PUBLIC_USE_MOCK_DATA === 'true') {
return tableUsers.filter(user => user.name.indexOf(param.name ?? '') > -1)
} else {
const response = await fetch('/api/table/users', {
method: 'POST',
body: JSON.stringify(param)
})
const {users} = await response.json()
return users
}
}
export default function TableScratch() {
const [searchWord, setSearchWord] = useTextInput()
const [tableUserData, setTableUserData] = useState<Array<TableUser>>([])
useEffect(() => {
(async () => {
const newUsers = await fetchUsers({ name: null })
setTableUserData(newUsers)
})()
}, [])
ハイライトした箇所でAPI呼び出しをしています。
ここで呼んでいるAPIは Next.js の API Routes で実装しています。
以下はそのコードです。
import { NextRequest, NextResponse } from "next/server"
import {tableUsers} from "@/data/data";
import {FetchTableUsersRequestType, TableUser} from "@/types/table";
export async function POST(request: NextRequest): Promise<NextResponse> {
const params: FetchTableUsersRequestType = await request.json()
const filteredUsers: TableUser[] = tableUsers.filter(user => user.name.indexOf(params.name ?? '') > -1)
return NextResponse.json({
users: filteredUsers
})
}
検索キーワードの入力欄と state
一覧を「名前」で絞り込むための入力欄を設けています。
このUIと state は、過去の記事で作成したコンポーネントとカスタムフックを使用しています。
export default function TableScratch() {
const [searchWord, setSearchWord] = useTextInput()
const [tableUserData, setTableUserData] = useState<Array<TableUser>>([])
========== 中略 ==========
return (
<main>
<Box m={2}>
<Box display="flex">
<Box>
<Input label="名前" errors={searchWord.errors} onChange={(e) => setSearchWord(e.target.value)} />
</Box>
今回は最大30字までのバリデーションをするようになっています。
バリデーションエラーがある場合は、検索ボタンを非活性表示になります。
このバリデーション機能を作ってみたい方は、こちらの記事も併せてご覧ください。
一覧データを state にセットする
画面の初期表示時と検索ボタンを押下時に検索処理が実行され、検索結果を state に反映します。
特に変わったところはなく、一般的な useState
の使い方ですね。
export default function TableScratch() {
const [searchWord, setSearchWord] = useTextInput()
const [tableUserData, setTableUserData] = useState<Array<TableUser>>([])
useEffect(() => {
(async () => {
const newUsers = await fetchUsers({ name: null })
setTableUserData(newUsers)
})()
}, [])
const handleClick = async () => {
const newUsers = await fetchUsers({ name: searchWord.value })
setTableUserData(newUsers)
}
一覧データを出力する
一覧データが取得できたので出力しましょう。
繰り返しで出力する場合はkey
属性のセットを忘れないようにしましょう。
<table>
<thead>
<tr>
<th>名前</th>
<th>所属</th>
<th>好きなもの</th>
</tr>
</thead>
<tbody>
{
tableUserData.map((user) => (
<tr key={user.id}>
<td>{user.name}</td>
<td>{user.department}</td>
<td>{user.favoriteThings}</td>
</tr>
))
}
</tbody>
</table>
おわりに
いかがだったでしょうか?
今回は、実務ではお馴染みの一覧画面の実装をしてみました。
職業プログラマであれば、一覧画面を実装する機会は多いと思うので、ぜひ今のうちにマスターしておきましょう。
ハコラトリは、Reactを習得したい駆け出しエンジニアを応援しています。
Reactレシピでは、これからもReactで色々なUIを作って紹介していきますので、「こんなUIを作ってみてほしい」「こんな場合はどうすれば?」といったアイディアを募集中です!
コメントお待ちしております。
コメント