1. 전역에 ModalProvider를 만든다.
- 서버사이드 렌더링으로 하이드레이션 이슈가 발생할 수 있기때문에 트릭을 걸어준다.
'use client';
import { useEffect, useState } from 'react';
import Modal from '@/components/Modal';
function ModalProvider() {
const [isMounted, setIsMounted] = useState(false);
// 모달은 서버에서 실행안되니깐 트릭걸어줘야함
useEffect(() => {
setIsMounted(true);
}, []);
// 서버에서는 null 보여줌
if (!isMounted) {
return null;
}
return (
<>
<Modal />
</>
);
}
export default ModalProvider;
2. Radix 사용
npm install @radix-ui/react-dialog
Radix로 기본이 되는 부모 모달 컴포넌트 생성
import * as Dialog from '@radix-ui/react-dialog';
import { ReactNode } from 'react';
import { IoMdClose } from 'react-icons/io';
interface ModalProps {
isOpen: boolean;
onChange: (open: boolean) => void;
title: string;
description: string;
children: ReactNode;
}
function Modal({ isOpen, onChange, title, description, children }: ModalProps) {
return (
<Dialog.Root open={isOpen} defaultOpen={isOpen} onOpenChange={onChange}>
<Dialog.Portal>
<Dialog.Overlay className='bg-neutral-900/90 backdrop-blur-sm fixed inset-0' />
<Dialog.Content className='fixed drop-shadow-md border border-neutral-700 top-[50%] left-[50%] max-h-full h-full md:h-auto md:max-h-[85vh] w-full md:w-[90vw] md:max-w-[450px] translate-x-[-50%] translate-y-[-50%] bg-neutral-800 p-[25px] focus:outline-none'>
<Dialog.Title className='text-xl text-center font-bold mb-4'>
{title}
</Dialog.Title>
<Dialog.Description className='mb-5 text-sm leading-normal text-center'>
{description}
</Dialog.Description>
<div>{children}</div>
<Dialog.Close asChild>
<button className='text-neutral-400 hover:text-white absolute top-[10px] right-[10px] inline-flex h-[25px] w-[25px] appearance-none items-center justify-center rounded-full focus:outline-none'>
<IoMdClose />
</button>
</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}
export default Modal;
자식이 되는 AuthModal 생성
'use client';
import Modal from './Modal';
function AuthModal() {
return (
<Modal
title='Welcome back'
description='Login to your account!'
isOpen
onChange={() => {}}
>
Auth Modal children
</Modal>
);
}
export default AuthModal;
AuthModal을 컨트롤하기 위한 전역 State 생성
import { create } from 'zustand';
interface AuthModalStore {
isOpen: boolean;
onOpen: () => void;
onClose: () => void;
}
const useAuthModal = create<AuthModalStore>((set) => ({
isOpen: false,
onOpen: () => set({ isOpen: true }),
onClose: () => set({ isOpen: false }),
}));
export default useAuthModal;