FullCalendar + shadcn Dialog で再レンダリングされるのを防ぐ

問題 FullCalendar と 同じページに shadcn Dialog を組み込み、 shadcn (Radix UI) Dialog の open ステートを useState で管理していると、 ダイアログの open/close 時に FullCalendar が再レンダリングされてしまう。 GoogleCalendarのイベントや、独自イベントがある場合、再レンダリングされると、 イベントが消えてしまう。 解決方法 ref, forwardRefs, useImperativeHandle を使って、再レンダリングを防ぐ。 以下詳細 まずは Dialog側 import { Ref, useMemo, useCallback, useState, forwardRef, useImperativeHandle, } from 'react'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, } from '@/components/ui/dialog'; type SampleProps = { }; export interface SampleDialogRefInterface { open: () => void; close: () => void; } const SampleDialogButtonInner = (_: SampleProps, ref: Ref<SampleDialogRefInterface>) => { const [isOpen, setIsOpen] = useState(false); useImperativeHandle(ref, () => ({ open: () => setIsOpen(true), close: () => setIsOpen(false), })); return ( <Dialog open={isOpen} onOpenChange={setIsOpen}> {/* 外部からOpenしたいので、DialogTriggerは使わない */} {/* <DialogTrigger asChild> <Button>ボタン</Button> </DialogTrigger> */} <DialogContent className='h-[90%] overflow-hidden'> {/* 省略 */} </DialogContent> </Dialog> ); // } }; const SampleDialogButton = forwardRef(SampleDialogButtonInner); export default SampleDialogButton; SampleProps 親側から渡す props SampleDialogRefInterface ダイアログとして公開したいメソッドを定義する _: SampleProps props は使わないので、_ で受け取る。propsがないとエラー ref: Ref<SampleDialogRefInterface> ref を受け取る const [isOpen, setIsOpen] = useState(false); ダイアログの open/close ステートを useState で管理 <Dialog open={isOpen} onOpenChange={setIsOpen}> isOpen, setIsOpen を Dialog の open, onOpenChange で紐付け useImperativeHandle ref として公開して、外部から呼ばれた場合の動作を定義 Dialogの開閉を外部から制御できるようにする const SampleDialogButton = forwardRef(SampleDialogButtonInner) forwardRef で ref を受け取るコンポーネントを作成 FullCalendar 側(親側) import { EventClickArg } from '@fullcalendar/core'; import FullCalendar from '@fullcalendar/react'; import { useRef, useCallback } from 'react'; import SampleDialogButton, { SampleDialogRefInterface, } from '@/app/_components/SampleDialogButton'; export default function Calendar() { const sampleDialogRef = useRef<SampleDialogRefInterface>(null); const onDateClick = useCallback( (arg: DateClickArg) => { const target = arg....

2024-09-03 ·  2024-10-23 · 2 分 · 302 文字