こんにちは、2022年4月入社の庄野です。今回は、新卒1年目の私がReact-TypeScript + Material-UI (以下、React,TS,MUI)の技術を使った、簡単なWEBアプリの作り方を3つの記事にわたって1から紹介したいと思います。
この記事はリスト表示編です。
対象
- React、TS初心者
- APIを扱ってみたい人
- カスタムフックを作成したい人
- MUIを使ってみたい人
やること
- プロジェクトの作成
- API用のモックサーバーを立てる
- APIを取得するカスタムフック作成
- MUIを使ってユーザーをリスト表示する
使用環境
開発環境は以下の通りです。
$ node --version
v16.17.1
$ npm --version
8.15.0
"@emotion/react": "^11.10.4",
"@emotion/styled": "^11.10.4",
"@mui/icons-material": "^5.10.6",
"@mui/lab": "^5.0.0-alpha.102",
"@mui/material": "^5.10.7",
"@stoplight/prism-cli": "^4.10.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"typescript": "^4.8.4",
前回
前回の記事では、モックサーバーを立てて、API取得するカスタムフックを作成しデータを他のコンポーネントでも扱えるようにしました。
今回
今回は、いよいよ取得したデータをMUIのコンポーネントを使ってリスト表示していきたいと思います。
リスト表示
それでは、各コンポーネントの作成に移ります。リスト表示するコンポーネントは以下のように、UserList
の中にUserCard
、さらにその中にUserIcon
のコンポーネントが含まれる構造となっており、UserListをUserCard、UserIconへと細分化して開発していきます。
アイコン
アイコンのコンポーネントでは、ユーザー(前回の記事でusertype.tsにおいて{name, status, thumbnail, useID}
の要素で定義)のpropsを受け取ってから、アイコン表示と、勤務状態に応じたステータスを表示します。完成図は以下のようになります。勤務状態によって、周りの色が変わります。(以下はステータスがworking
の場合)
特にこだわりなければ、勤務状態に応じたステータス表示はAvatar – Material UI – MUIのWith Badge(以下)を参考にしてよいと思います。私は、ステータスをアイコン周りの色で表現したかったので、Paper
コンポーネントの上にAvatar
コンポーネントを載せて実装しました。
ソースは以下。
import { memo, FC } from "react";
import Avatar from "@mui/material/Avatar";
import { Paper } from "@mui/material";
import { User } from "../types/usertype";
type Props = {
user: User; // ユーザー情報{name, status, thumbnail, useID}
size?: number; // アイコンの大きさ
};
export const UserIcon: FC<Props> = memo((props) => {
const { user, size = 50 } = props;
return (
<>
<Paper // アイコン周り外枠
sx={{
padding: "6px",
margin: 1,
borderRadius: "100%",
bgcolor: "#757575",
...(user.status === "working" && { // 勤務中の場合
bgcolor: "#70c4bc",
}),
...(user.status === "notWorking" && { // 退勤中の場合
bgcolor: "#ea8f8f",
}),
...(user.status === undefined && { // 不明の場合
bgcolor: "background.default",
}),
}}
>
<Avatar // アイコン
alt={user.name}
src={user.thumbnail}
sx={{ width: size, height: size, border: 1 }}
/>
</Paper>
</>
);
});
以上のようにすると、アイコンを簡単かつ綺麗に表示できたと思います。sx
propsでコンポーネントごとにcssをあてられるのもコードを簡潔にできる要因だと思います。ちなみに、アイコン周りの色を決めているbgcolor
もColor – Material UI – MUIから以下のように、実際に色味などを調整して選びました。
ユーザーカード
では、次にユーザーの情報を載せた以下のような”カード”を作ります。同じく、ユーザーのpropsを受け取り、先ほど作成したアイコンと、名前、勤務状態を表示します。
ソースは以下。
import { memo, FC } from "react";
import ListItemText from "@mui/material/ListItemText";
import ListItemIcon from "@mui/material/ListItemIcon";
import { MenuItem } from "@mui/material";
import { UserIcon } from "../atoms/UserIcon";
import { User } from "../types/usertype";
type Props = {
user: User;
};
export const UserCard: FC<Props> = memo((props) => {
const { user } = props; // ユーザー情報受け取り
return (
<>
<MenuItem>
<ListItemIcon>
<UserIcon user={user} /> // アイコン
</ListItemIcon>
<ListItemText
primary={user.name} // 名前
primaryTypographyProps={{ fontSize: { xs: 20, md: 40 } }}
secondary={ // 勤務状態
user.status === "working"
? "勤務中"
: user.status === "notWorking" && "退勤"
}
/>
</MenuItem>
</>
);
});
ListItemText
のpropsで、primary
とsecondary
に要素を渡すことによって特にデザインする必要なく、テキスト覧をいい感じに表示してくれます。React List component – Material UI – MUIのドキュメントで例(以下)がある通り、ListItemButton
、ListItemIcon
など、リストに載せたいボタンや、アイコンなど該当するコンポーネントでラップすれば、ある程度決まったデザインで表示できます。
ユーザーリスト
それでは、ユーザー一人を表示できるようになったので、リスト表示していきたいと思います。
また、勤務状態でメンバーをフィルタリングできるようにします。
import { useEffect, useState } from "react";
import { memo, FC } from "react";
import Paper from "@mui/material/Paper";
import MenuList from "@mui/material/MenuList";
import { Box, Tab } from "@mui/material";
import TabContext from "@mui/lab/TabContext";
import TabList from "@mui/lab/TabList";
import TabPanel from "@mui/lab/TabPanel";
import { UserCard } from "../molecules/UserCard";
import { useAllUsers } from "../hooks/useAllUsers";
export const UserList: FC = memo(() => {
const [value, setValue] = useState("1"); // タブ番号
const { getAllUsers, users } = useAllUsers(); // カスタムフック
const handleChange = (event: unknown, newValue: string) => { // タブ切り替え
setValue(newValue);
};
useEffect(() => {
getAllUsers(); // API取得
}, []);
return (
<Paper
sx={{
width: `80%`,
overflow: "auto",
m: 1,
borderRadius: 5,
boxShadow: 10,
}}
>
<Box sx={{ width: "100%", typography: "body1" }}>
<TabContext value={value}>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<TabList
onChange={handleChange} // 選択したタブに切り替える(valueを切り替え)
aria-label="lab API tabs example"
centered // 中央揃え
variant="fullWidth" // 最大横幅
>
// タブ
<Tab label="メンバー一覧" value="1" />
<Tab label="勤務中" value="2" />
<Tab label="退勤" value="3" />
</TabList>
</Box>
// 選択されたタブを表示(valueに応じて表示が変わる)
<TabPanel value="1">
<MenuList>
{users.map((user) => (
<UserCard user={user} key={user.userID} />
))}
</MenuList>
</TabPanel>
<TabPanel value="2">
<MenuList>
{users.map(
(user) =>
user.status === "working" && (
<UserCard user={user} key={user.userID} />
)
)}
</MenuList>
</TabPanel>
<TabPanel value="3">
<MenuList>
{users.map(
(user) =>
user.status === "notWorking" && (
<UserCard user={user} key={user.userID} />
)
)}
</MenuList>
</TabPanel>
</TabContext>
</Box>
</Paper>
);
});
まず、前の記事で実装したuseAllUsers
のカスタムフックで表示するユーザーのデータを取得します。次にタブが選択されるたびに更新されるvalue
をuseStateで定義し、そのvalue
に応じて表示内容を変えています。あとは、mapメソッドで条件によって、表示するユーザーを変えれば完成です。
導入編の記事で、すでにMemberList.tsx
はMain
コンポーネントに内包されているので、表示できるようになっていると思います。
結果
お疲れ様です。
以下の動画のようなものができたと思います。
さいごに
以上3つの記事で、カスタムフックを使ってユーザーのAPIを取得し、ユーザーをリスト表示するWEBアプリを作ってみました。
カスタムフックは、ビューとロジックを切り分けて書けるのが一番の魅力でした。また、MUIはドキュメントが優秀と感じました。使用例が充実しているため、コンポーネントの使い方を知るより、コピペで少し手直しするだけでもそれなりのUIができてしまいます。すでにあるものを再利用できるReactだからこそですね。
以上です。