はじめに
お疲れ様です、りつです。
今日はChakra UI v3でモーダルを実装する方法について備忘録もかねてご紹介します。
問題
現在以下のようなユーザー一覧画面を実装しております。
今回実装したかった機能は、ユーザー一覧にあるカードをクリックした際に、対象のユーザー詳細モーダルを表示するといったものです。
参考にしている学習動画がChakra v1のModal
コンポーネントを使用しているのですが、Chakra v3ではDialog
コンポーネントに名称が変更されていました。
Modal
- Renamed to
Dialog
- Remove
isCentered
prop in favor of using theplacement=center
prop- Removed
isOpen
andonClose
props in favor of using theopen
andonOpenChange
props
また、Chakra UIの公式ドキュメントだとDialogTrigger
コンポーネントでButton
コンポーネントを囲んで、そのボタンを押下した際にモーダルを表示させるサンプルが多いです。
ただ、今回の実装で同じ記述方法をしてしまうと、モーダル表示部分だけコンポーネント化するのが難しくなってしまいそうでした。
そのため、以下のControlled
のサンプルを元に、open
とonOpenChange
プロパティを使用して調整を行いました。
解決方法
まず一覧画面の実装が以下の通りです。
以下の処理をそれぞれコンポーネント化し、呼び出しています。
- ユーザーカード:
UserCard
コンポーネント - モーダル:
UserDetailModal
コンポーネント
また、open
とsetOpen
というモーダルの開閉管理用のステートを用意しています。
UserCard
へ渡すクリックイベント(onClickUser
)で、setOpen={true}
を指定することでモーダルを開くことができます。
import React, { memo, useCallback, useEffect, useState } from "react";
import { Center, Flex, HStack, Spinner } from "@chakra-ui/react";
import { UserCard } from "@/components/organisms/user/UserCard";
import { UserDetailModal } from "@/components/organisms/user/UserDetailModal";
import { useAllUsers } from "@/hooks/useAllUsers";
import { useSelectUser } from "@/hooks/useSelectUser";
export const UserManagement: React.FC = memo(() => {
const [open, setOpen] = useState(false);
const { getUsers, loading, users } = useAllUsers();
const { onSelectUser, selectedUser } = useSelectUser();
useEffect(() => getUsers(), []);
const onClickUser = useCallback((id: number) => {
onSelectUser({ id, users });
setOpen(true);
}, [users, onSelectUser]);
return (
<>
{ loading ? (
<Center h="100vh">
<Spinner />
</Center>
) : (
<HStack wrap="wrap" p={{ base: 4, md: 10 }}>
{ users.map((user) => (
<Flex align='flex-start' key={user.id} mx="auto">
<UserCard
id={user.id}
imageUrl="https://picsum.photos/200"
userName={user.username}
fullName={user.name}
onClick={onClickUser}
/>
</Flex>
)) }
</HStack>
) }
<UserDetailModal user={selectedUser} open={open} setOpen={setOpen} />
</>
);
});
以下が、UserCard
コンポーネントの内容です。
import React, { memo } from "react";
import { Box, Image, Stack, Text } from "@chakra-ui/react";
type Props = {
id: number;
imageUrl: string;
userName: string;
fullName: string;
onClick: (id: number) => void;
};
export const UserCard: React.FC<Props> = memo((props) => {
const { id, imageUrl, userName, fullName, onClick } = props;
return (
<Box
w="260px"
h="260px"
bg="white"
borderRadius="10px"
shadow="md"
p={4}
_hover={{ cursor: "pointer", opacity: 0.8 }}
onClick={() => onClick(id)}
>
<Stack textAlign="center">
<Image
borderRadius="full"
boxSize="160px"
src={imageUrl}
alt={userName}
m="auto" />
<Text fontSize="lg" fontWeight="bold">
{userName}
</Text>
<Text fontSize="sm" color="gray">
{fullName}
</Text>
</Stack>
</Box>
);
});
最後に、モーダル表示部分をコンポーネント化したのが以下のUserDetailModal
コンポーネントです。
ユーザー一覧画面(UserManagement.tsx
)で渡されたモーダル開閉管理用のステートを受け取り、それをもとにDialogRoot
コンポーネントのopen
とonOpenChange
プロパティを指定しています。
import React, { memo } from "react";
import { Input, Stack } from "@chakra-ui/react";
import {
DialogBody,
DialogCloseTrigger,
DialogContent,
DialogHeader,
DialogRoot,
DialogTitle,
} from "@/components/ui/dialog"
import { Field } from "@/components/ui/field";
import { User } from "@/types/api/user";
type Props = {
user: User | null;
open: boolean;
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
};
export const UserDetailModal: React.FC<Props> = memo((props) => {
const { user, open, setOpen } = props;
return (
<DialogRoot lazyMount
open={open}
onOpenChange={(e) => setOpen(e.open)}
motionPreset="slide-in-bottom"
trapFocus={false}
>
<DialogContent pb={6}>
<DialogHeader>
<DialogTitle>ユーザー詳細</DialogTitle>
</DialogHeader>
<DialogBody mx={4}>
<Stack gap={4}>
<Field label="名前">
<Input value={user?.username} readOnly />
</Field>
<Field label="フルネーム">
<Input value={user?.name} readOnly />
</Field>
<Field label="MAIL">
<Input value={user?.email} readOnly />
</Field>
<Field label="TEL">
<Input value={user?.phone} readOnly />
</Field>
</Stack>
</DialogBody>
<DialogCloseTrigger />
</DialogContent>
</DialogRoot>
);
});
おわりに
今回はChakra UI v3のモーダル表示方法についてご紹介しました。
上記ではソースコードを抜粋しておりましたが、細かい実装について参考にしたい方は以下のリポジトリもご参照ください。
参考