1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Chakra UI v3 × React 19 × TypeScript】モーダルが画面に表示されない

Posted at

はじめに

こちらのUdemy教材でTypeScriptのハンズオン学習を行っていたところ、Chakra UI v3を使用した環境下でのモーダル表示にハマったので、備忘録を兼ねて記事にします。

事象

画像のようなカード方式のユーザー一覧画面に、ユーザーカードをクリックするとユーザー情報詳細が見られるモーダルを実装しようとしました。

ユーザー一覧画面

しかし、ユーザーカードをクリックしてもバックドロップのみ表示され、肝心のモーダルが表示されませんでした。

UserManagementApp_ModalError.gif

関連コード

src/components/pages/UserManagement.tsx
/* 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} />
    </>
  );
});
src/components/organisms/user/UserDetailModal.tsx
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にすると、ユーザーカードの下からモーダルが出てきました!

overflow: visible で表示されたモーダル。ユーザーカードの下にはみ出していて隠れていた

つまり、モーダルは表示されていなかったのではなく、表示されていたけどはみ出していて隠れていたのでした。

解決方法

色々試してみましたがどれも上手くいかず、結局styleで直接表示位置を修正することにしました。

src/components/organisms/user/UserDetailModal.tsx
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>
  );
});

これでモーダルが無事表示されました。

image.png

おわりに

今回はほぼ独力でエラー箇所を切り分けながら原因箇所を特定しましたが、作業にも少しずつ慣れ、自信がついてきたせいかエラー分析が楽しくなってきました。
もちろん、エラーが起きないに越したことはないのですが。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?