Notice
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
Tags
- AWS
- 에러
- Project
- img upload
- GIT
- 리액트 네이티브
- s3 upload
- Next.js
- error
- aws bucket 정책
- 문자열 대소문자 구별
- firebase 라이브러리
- 문자열 대소문자
- React
- AWS Access Key
- react native font
- Access Key 생성
- react native
- 리액트 네이티브 에러
- react native 세팅
- fire base
- PongWorld
- react native picker
- 리액트
- react native 개발
- react native CLI
- js
- babel.config.js
- 백준
- 리엑트 네이티브 아이콘
Archives
- Today
- Total
밝을희 클태
[ React ] Custom Confirm Modal 만들기 본문
프로젝트에서 기본 Confirm Modal을 사용하고 있었는데 일단 못생기기도 했고 모바일에서 대화창 숨기기랑 항목이 데스크톱과 다르게 있는데 이 항목을 한번 클릭하면 더 이상 Confirm Modal이 뜨지 않는다. 우리 프로젝트에서 Confirm Modal의 입력을 꼭 "예" 를 받아다 다음 상황으로 진행이 되기 때문에 실수로 대화창 숨기기를 클릭해 버리면 사용자는 왜 다음 상황으로 안 넘어가는지 알 수 없는 문제가 발생했다.
그래서 useContext API를 사용해 전역에서 사용가능한 Confirm Modal을 만들어 볼거다.
ModalProvider.jsx
형태 | 용도 | |
modalMessage | String | 모달의 메인 메세지 |
modalSubMessage | String | 모달의 서브 메세지 |
isSelectableModal | boolean | 선택 가능한 모달 여부 |
resolvePromise | function | 'Promise'를 완료 하는 함수 |
'use client'; import { usePathname } from 'next/navigation'; import { createContext, useCallback, useContext, useEffect, useState } from 'react'; const ModalContext = createContext(); export const useModal = () => useContext(ModalContext); export const ModalProvider = ({ children }) => { const [isModalOpen, setIsModalOpen] = useState(false); const [modalMessage, setModalMessage] = useState(''); const [modalSubMessage, setModalSubMessage] = useState(''); const [isSelectableModal, setIsSelectableModal] = useState(false); const [size, setSize] = useState('w-72'); const [resolvePromise, setResolvePromise] = useState(null); const pathName = usePathname(); const openModal = useCallback(({ message, subMessage = '', isSelectable = false, size }) => { setModalMessage(message); setModalSubMessage(subMessage); setIsSelectableModal(isSelectable); setIsModalOpen(true); if (size) setSize(size); return new Promise(resolve => { setResolvePromise(() => resolve); }); }, []); const resetModalState = useCallback(() => { setModalMessage(''); setModalSubMessage(''); setIsSelectableModal(false); setSize('w-72'); }, []); const closeModal = useCallback(() => { if (resolvePromise) { resolvePromise(false); setResolvePromise(null); } setIsModalOpen(false); resetModalState(); }, [resolvePromise]); const confirmModal = useCallback(() => { if (resolvePromise) { resolvePromise(true); setResolvePromise(null); } setIsModalOpen(false); resetModalState(); }, [resolvePromise]); useEffect(() => closeModal(), [pathName]); return ( <ModalContext.Provider value={{ isModalOpen, modalMessage, modalSubMessage, openModal, closeModal, confirmModal, isSelectableModal, size, }} > {children} </ModalContext.Provider> ); };
Modal.jsx
'use client'; import { useModal } from './ModalProvider'; export default function Modal() { const { isModalOpen, modalMessage, modalSubMessage, closeModal, confirmModal, isSelectableModal, size } = useModal(); if (!isModalOpen) return null; return ( <div className="fixed top-0 left-0 w-screen custom-dvh flex justify-center items-center bg-black bg-opacity-50 z-50" onClick={e => { if (e.currentTarget === e.target) closeModal(); }} > <div className={`${size} rounded space-y-4 p-3 bg-white flex flex-col justify-center items-center border-solid border`} > <div className="space-y-2"> <p className="font-semibold text-lg text-center break-all whitespace-pre-line">{modalMessage}</p> {modalSubMessage && ( <p className="text-center text-gray-400 text-sm whitespace-pre-line">{modalSubMessage}</p> )} </div> <div className="flex justify-center space-x-2 h-10 font-semibold"> <button className={`flex rounded justify-center items-center w-32 ${ isSelectableModal ? 'bg-gray-200' : 'bg-black text-white' } `} onClick={closeModal} > {isSelectableModal ? '취소' : '확인'} </button> {isSelectableModal && ( <button className="flex rounded justify-center items-center w-32 bg-black text-white" onClick={confirmModal} > 확인 </button> )} </div> </div> </div> ); }
실제 사용 예시
아래와 같이 ModalProvier와 Modal을 Layout 컴포넌트에 추가해준다.
export default function MainLayout({ children }) { return ( <ModalProvider> {children} <Modal /> </ModalProvider> ); }
import { useModal } from '@/app/(main)/_components/ModalProvider'; export default function Page() { const { openModal } = useModal(); const openConfirmModal = async () => { const res = await openModal({ message: 'hello world?', isSelectable: true, }); if (!res) return; // 추가 작업이 여기에 위치 }; return <button onClick={openConfirmModal}>모달 열기</button>; }

'KEYNUT 프로젝트' 카테고리의 다른 글
[ Next.JS ] userAgent를 효율적으로 관리해보자 (1) | 2024.09.04 |
---|---|
내가 설정한 description이 안 나온다. (0) | 2024.08.28 |
[ Next.JS ] PWA 설정하기 feat. Please check your GenerateSW plugin configuration:[WebpackGenerateSW] 'images' property is not expected to be here. Did you mean property 'mode'? (0) | 2024.08.19 |
[ AWS ] CloudFront를 활용해 S3 GET요청 최적화 (0) | 2024.08.18 |
[ Next.JS ] 이미지 슬라이더(image-slider) UX 개선 (0) | 2024.08.18 |