結論
Wrapperをつくる
まえがき
ついにChakra v3が来ましたね。framer-motionの依存がなくなり、軽量かつ高速になったようです。しかし、当然ながら破壊的変更が多く、移行のコストが大きいと感じました。そこで、今回は移行を進めていく際の方針を共有します。
V2Modal
import { useDisclosure } from "@chakra-ui/react";
import {
Button,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
} from "@chakra-ui/react";
export const TestModal = () => {
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<>
<Button onClick={onOpen} size={"sm"} closeOnEsc={true}>
Open
</Button>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>header</ModalHeader>
<ModalCloseButton />
<ModalBody>body</ModalBody>
<ModalFooter>
<Button colorScheme="blue" mr={3} onClick={onClose}>
Close
</Button>
<Button variant="ghost">Secondary Action</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
超基本的なモーダルですね。V3ではModalがなくなりDialogになっています。いろいろ変わってしまっており、このままでは動きません。
Wrapperを用意する
import {
DialogBackdrop,
DialogBody,
DialogCloseTrigger,
DialogContent,
DialogFooter,
DialogHeader,
DialogRoot,
} from "@/components/ui/dialog";
type ModalProps = {
children: React.ReactNode;
isOpen: boolean;
onClose: () => void;
closeOnEsc?: boolean;
};
export const Modal = ({
children,
isOpen,
onClose,
closeOnEsc,
}: ModalProps) => {
const onOpenClose = () => {
if (isOpen) {
onClose();
}
};
return (
<DialogRoot
open={isOpen}
onOpenChange={onOpenClose}
closeOnEscape={closeOnEsc}
>
{children}
</DialogRoot>
);
};
export const ModalBody = DialogBody;
export const ModalCloseButton = DialogCloseTrigger;
export const ModalOverlay = DialogBackdrop;
export const ModalHeader = DialogHeader;
export const ModalFooter = DialogFooter;
export const ModalContent = DialogContent;
こういうwrapperを用意すれば、呼び出し元はimport先を変更するだけで大丈夫です。
WrapperからModalを呼び出す
import { Button, useDisclosure } from "@chakra-ui/react";
import {
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
} from "../components/v2/Modal";
export const TestModal = () => {
const { open: isOpen, onOpen, onClose } = useDisclosure();
return (
<>
<Button onClick={onOpen} size={"sm"}>
Open
</Button>
<Modal isOpen={isOpen} onClose={onClose} closeOnEsc={true}>
<ModalOverlay />
<ModalContent>
<ModalHeader>header</ModalHeader>
<ModalCloseButton />
<ModalBody>body</ModalBody>
<ModalFooter>
<Button colorScheme="blue" mr={3} onClick={onClose}>
Close
</Button>
<Button variant="ghost">Secondary Action</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
import先が変わっただけで、他の部分は何も変えなくて良いですね。useDisclosureがisOpen -> openに変更されていたり、colorSchemeがcolorPaletteになっていたり、isDisabledがdisabledになっていたり、closeOnEscがcloseOnEscapeになっていたりと細かい変更点があります。こういう部分も含めたWrapperを書いてあげると良いでしょう。
というかV2とV3を混在させたい場合
あんまりおすすめしませんが(バンドルサイズもやばいことになるし)、やるなら使うコンポーネントだけをV2からインポートしてエクスポートするだけのWrapperライブラリを作成します。V2のコンポーネントを使うときだけは、そのWrapperライブラリからインポートするようにすればV2とV3が共存できます。やらんほうが良いと思うけど一応。
新しいコンポーネントを書くときにV3を利用しつつ、いままで書いてきたコンポーネントたちはWrapperでとりあえず動く状態にする、というのが一番手軽だと思います。でもcolorPaletteが変わっていたり、inputのデザインが変わっていたりと非常に破壊的な変更があるため、全然違うデザインになってしまうなぁ、という問題があります。いまのプロジェクトはV2系でいって、次のプロジェクトからV3にするとかの選択肢もあるでしょうね。そのほうが楽かな……。
chakra v3にしてみたら700個くらいtypeエラーが出て諦めそうになりましたが、Wrapperを書く方針にしてからは2時間くらいでなんとかなりました。デザイン変わりすぎたので、そのブランチはお蔵入りになりましたが……。