はじめに
こちらのUdemy教材でTypeScriptのハンズオン学習を行っていたところ、Chakra UI v3を使用した環境下でのモーダル表示にハマったので、備忘録を兼ねて記事にします。
事象
画像のようなカード方式のユーザー一覧画面に、ユーザーカードをクリックするとユーザー情報詳細が見られるモーダルを実装しようとしました。
しかし、ユーザーカードをクリックしてもバックドロップのみ表示され、肝心のモーダルが表示されませんでした。
関連コード
/* eslint-disable react-hooks/exhaustive-deps */
import { FC, memo, useCallback, useEffect, useState } from "react";
import { Center, Spinner, Wrap, WrapItem } from "@chakra-ui/react";
import { UserCard } from "../organisms/user/UserCard";
import { UserDetailModal } from "../organisms/user/UserDetailModal";
import { useAllUsers } from "@/hooks/useAllUsers";
import { useSelectUser } from "@/hooks/useSelectUser";
export const UserManagement: FC = memo(() => {
const [open, setOpen] = useState(false);
const { getUsers, users, loading } = useAllUsers();
const { onSelectUser, selectedUser } = useSelectUser();
console.log(selectedUser);
useEffect(() => getUsers(), []);
const onClickUser = useCallback(
(id: number) => {
onSelectUser({ id, users, setOpen });
},
[users, onSelectUser, setOpen]
);
return (
<>
{loading ? (
<Center h="100vh">
<Spinner />
</Center>
) : (
<Wrap p={{ base: 4, md: 10 }}>
{users.map((user) => (
<WrapItem key={user.id} mx="auto">
<UserCard
id={user.id}
imageUrl="https://picsum.photos/300"
userName={user.username}
fullName={user.name}
onClick={onClickUser}
/>
</WrapItem>
))}
</Wrap>
)}
<UserDetailModal user={selectedUser} open={open} setOpen={setOpen} />
</>
);
});
import { FC, memo } from "react";
import { CloseButton, Dialog, Field, Input, Stack } from "@chakra-ui/react";
import { User } from "../../../types/api/user";
type Props = {
user: User | null;
open: boolean;
setOpen: (open: boolean) => void;
};
export const UserDetailModal: FC<Props> = memo((props) => {
const { user, open, setOpen } = props;
return (
<Dialog.Root
lazyMount
open={open}
onOpenChange={(e) => setOpen(e.open)}
motionPreset="slide-in-bottom"
trapFocus={false}
>
<Dialog.Backdrop />
<Dialog.Content pb={6}>
<Dialog.Title>ユーザー詳細</Dialog.Title>
<Dialog.Body mx={4}>
<Stack gap={4}>
<Field.Root>
<Field.Label>名前</Field.Label>
<Input value={user?.username} readOnly />
</Field.Root>
<Field.Root>
<Field.Label>フルネーム</Field.Label>
<Input value={user?.name} readOnly />
</Field.Root>
<Field.Root>
<Field.Label>MAIL</Field.Label>
<Input value={user?.email} readOnly />
</Field.Root>
<Field.Root>
<Field.Label>TEL</Field.Label>
<Input value={user?.phone} readOnly />
</Field.Root>
</Stack>
</Dialog.Body>
<Dialog.CloseTrigger asChild>
<CloseButton />
</Dialog.CloseTrigger>
</Dialog.Content>
</Dialog.Root>
);
});
分析
ソースマップエラーが出ていたこともあり、当初はAPI周りを疑っていましたが、固定値で表示されるモーダルの下が切れていることに気付き、表示の問題を疑いました。
そこで、元のコードの動作を開発者ツールで再度確認すると、モーダルの要素自体は生成されていることがわかりました。そこで、要素をoverflow: visible
にすると、ユーザーカードの下からモーダルが出てきました!
つまり、モーダルは表示されていなかったのではなく、表示されていたけどはみ出していて隠れていたのでした。
解決方法
色々試してみましたがどれも上手くいかず、結局style
で直接表示位置を修正することにしました。
import { FC, memo } from "react";
import { CloseButton, Dialog, Field, Input, Stack } from "@chakra-ui/react";
import { User } from "@/types/api/user";
type Props = {
user: User | null;
open: boolean;
setOpen: (open: boolean) => void;
};
export const UserDetailModal: FC<Props> = memo((props) => {
const { user, open, setOpen } = props;
return (
<Dialog.Root
lazyMount
open={open}
onOpenChange={(e) => setOpen(e.open)}
motionPreset="slide-in-bottom"
trapFocus={false}
>
<Dialog.Backdrop />
<Dialog.Content
pb={6}
+ style={{
+ position: "fixed",
+ top: "50%",
+ left: "50%",
+ transform: "translate(-50%, -50%)",
+ }}
>
<Dialog.Title>ユーザー詳細</Dialog.Title>
<Dialog.Body mx={4}>
<Stack gap={4}>
<Field.Root>
<Field.Label>名前</Field.Label>
<Input value={user?.username} readOnly />
</Field.Root>
<Field.Root>
<Field.Label>フルネーム</Field.Label>
<Input value={user?.name} readOnly />
</Field.Root>
<Field.Root>
<Field.Label>MAIL</Field.Label>
<Input value={user?.email} readOnly />
</Field.Root>
<Field.Root>
<Field.Label>TEL</Field.Label>
<Input value={user?.phone} readOnly />
</Field.Root>
</Stack>
</Dialog.Body>
<Dialog.CloseTrigger asChild>
<CloseButton />
</Dialog.CloseTrigger>
</Dialog.Content>
</Dialog.Root>
);
});
これでモーダルが無事表示されました。
おわりに
今回はほぼ独力でエラー箇所を切り分けながら原因箇所を特定しましたが、作業にも少しずつ慣れ、自信がついてきたせいかエラー分析が楽しくなってきました。
もちろん、エラーが起きないに越したことはないのですが。