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

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

https://www.udemy.com/course/react_stepup/

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

![ユーザー一覧画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3941608/5d9e1a3c-5156-4ab9-9b24-40477db78101.png)

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

![UserManagementApp_ModalError.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3941608/7fd510e4-898e-45d7-b682-41c0ac4edf82.gif)

## 関連コード
```react: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} />
    </>
  );
});
```

```react: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周りを疑っていましたが、固定値で表示されるモーダルの下が切れていることに気付き、表示の問題を疑いました。

![固定値で表示されたモーダル。下の部分が切れている](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3941608/4f42c345-bb9b-4a2a-b6e7-5ad253d96d9e.png)

そこで、元のコードの動作を開発者ツールで再度確認すると、モーダルの要素自体は生成されていることがわかりました。そこで、要素を`overflow: visible`にすると、ユーザーカードの下からモーダルが出てきました！

![overflow: visible で表示されたモーダル。ユーザーカードの下にはみ出していて隠れていた](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3941608/36dd83bd-4a5c-4412-a4ab-cfbd48ca1c66.png)

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

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

```diff_typescript: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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3941608/5fe06a8b-2784-4d4e-85b3-c9eb889633a2.png)

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