【React-TS + MUI】③メンバーリストのWEBアプリを作成してみた

こんにちは、2022年4月入社の庄野です。今回は、新卒1年目の私がReact-TypeScript + Material-UI (以下、React,TS,MUI)の技術を使った、簡単なWEBアプリの作り方を3つの記事にわたって1から紹介したいと思います。

この記事はリスト表示編です。

  1. 導入編
  2. カスタムフック編
  3. リスト表示編

対象

  • 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をあてられるのもコードを簡潔にできる要因だと思います。ちなみに、アイコン周りの色を決めているbgcolorColor – 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で、primarysecondaryに要素を渡すことによって特にデザインする必要なく、テキスト覧をいい感じに表示してくれます。React List component – Material UI – MUIのドキュメントで例(以下)がある通り、ListItemButtonListItemIconなど、リストに載せたいボタンや、アイコンなど該当するコンポーネントでラップすれば、ある程度決まったデザインで表示できます。

ユーザーリスト

それでは、ユーザー一人を表示できるようになったので、リスト表示していきたいと思います。
また、勤務状態でメンバーをフィルタリングできるようにします。

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.tsxMainコンポーネントに内包されているので、表示できるようになっていると思います。

結果

お疲れ様です。

以下の動画のようなものができたと思います。

さいごに

以上3つの記事で、カスタムフックを使ってユーザーのAPIを取得し、ユーザーをリスト表示するWEBアプリを作ってみました。
カスタムフックは、ビューとロジックを切り分けて書けるのが一番の魅力でした。また、MUIはドキュメントが優秀と感じました。使用例が充実しているため、コンポーネントの使い方を知るより、コピペで少し手直しするだけでもそれなりのUIができてしまいます。すでにあるものを再利用できるReactだからこそですね。

以上です。

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です