Skip to content

Commit

Permalink
모달 관련 오류 해결 (#427)
Browse files Browse the repository at this point in the history
* feat: 오류 수정

* feat: Show 컴포넌트 추가

* feat: ModalBackground props 타입 제공으로 인한 재사용성 증가

* refactor: 안쓰는 import 제거

* fix: 모달 오픈 여부를 판단하여 중첩모달시에도 모달이 남아있으면 스크롤이 안되게 변경

* feat: modal dependency 변경

* feat: useOverlay 를 걷어내고 Nest구조로 변경

* chore: Show 컴포넌트 제거

* feat: overlay exit 인터페이스 닫기

* feat: EmptyView에서도 적용 방식 변경
  • Loading branch information
na-reum authored Sep 24, 2023
1 parent c843e8f commit fe13016
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 83 deletions.
1 change: 1 addition & 0 deletions pages/detail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const DetailPage = () => {
<>
<SDetailPage>
<Carousel imageList={detailData?.imageURL} />

<DetailHeader
detailData={detailData}
mutateMeetingDeletion={mutateDeleteMeeting}
Expand Down
7 changes: 5 additions & 2 deletions src/components/modal/ModalBackground.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Box } from '@components/box/Box';
import { ComponentProps } from 'react';
import { styled } from 'stitches.config';

const ModalBackground = () => {
return <SModalBackground />;
type ModalBackgroundProps = ComponentProps<typeof Box>;

const ModalBackground = (props: ModalBackgroundProps) => {
return <SModalBackground {...props} />;
};

export default ModalBackground;
Expand Down
55 changes: 17 additions & 38 deletions src/components/page/meetingDetail/Feed/EmptyView.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,32 @@
import { Box } from '@components/box/Box';
import { styled } from 'stitches.config';
import { useOverlay } from '@hooks/useOverlay/Index';
import FeedCreateModal from '@components/page/meetingDetail/Feed/Modal/FeedCreateModal';
import ConfirmModal from '@components/modal/ConfirmModal';
import useModal from '@hooks/useModal';

interface EmptyViewProps {
isMember: boolean;
}

const EmptyView = ({ isMember }: EmptyViewProps) => {
const createFeedOverlay = useOverlay();
const closeCreateFeedOverlay = useOverlay();
const feedCreateModal = useModal();

const handleCreateFeedOpen = () =>
createFeedOverlay.open(({ isOpen: isCreateModalOpen, close: closeCreateModal }) => (
return (
<>
<SContent>
<p>아직 작성된 피드가 없어요.</p>

{isMember && (
<>
<p>첫번째 작성자가 되어볼까요?</p>
<button onClick={feedCreateModal.handleModalOpen}>작성하러 가기</button>
</>
)}
</SContent>
<FeedCreateModal
isModalOpened={isCreateModalOpen}
handleModalExit={() => {
closeCreateModal();
}}
handleModalClose={() => {
closeCreateFeedOverlay.open(({ isOpen: isConfirmModalOpen, close: closeConfirmModal }) => (
<ConfirmModal
isModalOpened={isConfirmModalOpen}
message={`피드 작성을 그만두시겠어요?\n지금까지 쓴 내용이 지워져요.`}
handleModalClose={closeConfirmModal}
cancelButton="돌아가기"
confirmButton="그만두기"
handleConfirm={() => {
closeConfirmModal();
closeCreateModal();
}}
/>
));
}}
isModalOpened={feedCreateModal.isModalOpened}
handleModalClose={feedCreateModal.handleModalClose}
/>
));

return (
<SContent>
<p>아직 작성된 피드가 없어요.</p>

{isMember && (
<>
<p>첫번째 작성자가 되어볼까요?</p>
<button onClick={handleCreateFeedOpen}>작성하러 가기</button>
</>
)}
</SContent>
</>
);
};

Expand Down
72 changes: 43 additions & 29 deletions src/components/page/meetingDetail/Feed/Modal/FeedCreateModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,24 @@ import { Box } from '@components/box/Box';
import ModalContainer, { ModalContainerProps } from '@components/modal/ModalContainer';
import FeedFormPresentation from './FeedFormPresentation';
import { FormType, schema } from './schema';
import { useOverlay } from '@hooks/useOverlay/Index';
import ConfirmModal from '@components/modal/ConfirmModal';
import { useMutation } from '@tanstack/react-query';
import { createPost } from '@api/post';
import { useQueryGetMeeting } from '@api/meeting/hooks';
import { useRouter } from 'next/router';
import useModal from '@hooks/useModal';
import { useEffect } from 'react';

const DevTool = dynamic(() => import('@hookform/devtools').then(module => module.DevTool), {
ssr: false,
});

interface FeedCreateModalProps extends ModalContainerProps {
handleModalExit: () => void;
}

function FeedCreateModal({ isModalOpened, handleModalExit, handleModalClose }: FeedCreateModalProps) {
const submitCreateFeedOverlay = useOverlay();
function FeedCreateModal({ isModalOpened, handleModalClose }: ModalContainerProps) {
const router = useRouter();
const id = router.query.id as string;
const { data: detailData } = useQueryGetMeeting({ params: { id } });
const exitModal = useModal();
const submitModal = useModal();

const formMethods = useForm<FormType>({
mode: 'onChange',
Expand All @@ -45,30 +43,27 @@ function FeedCreateModal({ isModalOpened, handleModalExit, handleModalClose }: F
formMethods.setValue('images', images);
};

const onSubmit: SubmitHandler<FormType> = async formData => {
submitCreateFeedOverlay.open(({ isOpen, close }) => (
<ConfirmModal
isModalOpened={isOpen}
message="게시글을 작성하시겠습니까?"
handleModalClose={close}
cancelButton="돌아가기"
confirmButton="확인"
handleConfirm={async () => {
const createFeedParameter = { ...formData, meetingId: Number(id) };
await mutateCreateFeed(createFeedParameter, {
onSuccess: () => {
alert('피드를 작성했습니다.');
close();
handleModalExit();
},
});
}}
/>
));
const handleSubmitClick: SubmitHandler<FormType> = () => {
submitModal.handleModalOpen();
};

const onSubmit = async () => {
const createFeedParameter = { ...formMethods.getValues(), meetingId: Number(id) };
await mutateCreateFeed(createFeedParameter, {
onSuccess: () => {
alert('피드를 작성했습니다.');
submitModal.handleModalClose();
handleModalClose();
},
});
};

useEffect(() => {
formMethods.reset();
}, [isModalOpened]);

return (
<ModalContainer isModalOpened={isModalOpened} handleModalClose={handleModalClose}>
<ModalContainer isModalOpened={isModalOpened} handleModalClose={exitModal.handleModalOpen}>
<SDialogWrapper>
<FormProvider {...formMethods}>
<FeedFormPresentation
Expand All @@ -80,11 +75,30 @@ function FeedCreateModal({ isModalOpened, handleModalExit, handleModalClose }: F
title="피드 작성"
handleDeleteImage={handleDeleteImage}
handleModalClose={handleModalClose}
onSubmit={formMethods.handleSubmit(onSubmit)}
onSubmit={formMethods.handleSubmit(handleSubmitClick)}
disabled={isSubmitting || !isValid}
/>
</FormProvider>
</SDialogWrapper>
<ConfirmModal
isModalOpened={exitModal.isModalOpened}
message={`피드 작성을 그만두시겠어요?\n지금까지 쓴 내용이 지워져요.`}
handleModalClose={exitModal.handleModalClose}
cancelButton="돌아가기"
confirmButton="그만두기"
handleConfirm={() => {
exitModal.handleModalClose();
handleModalClose();
}}
/>
<ConfirmModal
isModalOpened={submitModal.isModalOpened}
message="게시글을 작성하시겠습니까?"
handleModalClose={submitModal.handleModalClose}
cancelButton="돌아가기"
confirmButton="확인"
handleConfirm={onSubmit}
/>
{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
{/* @ts-ignore */}
<DevTool control={formMethods.control} />
Expand Down
15 changes: 3 additions & 12 deletions src/hooks/useOverlay/Index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ import { CreateOverlayElement } from './types';

let elementId = 1;

interface Options {
exitOnUnmount?: boolean;
}

export function useOverlay({ exitOnUnmount = true }: Options = {}) {
export function useOverlay() {
const context = useContext(OverlayContext);

if (context == null) {
Expand All @@ -23,11 +19,9 @@ export function useOverlay({ exitOnUnmount = true }: Options = {}) {

useEffect(() => {
return () => {
if (exitOnUnmount) {
unmount(id);
}
unmount(id);
};
}, [exitOnUnmount, id, unmount]);
}, [id, unmount]);

return useMemo(
() => ({
Expand All @@ -47,9 +41,6 @@ export function useOverlay({ exitOnUnmount = true }: Options = {}) {
close: () => {
overlayRef.current?.close();
},
exit: () => {
unmount(id);
},
}),
[id, mount, unmount]
);
Expand Down
4 changes: 3 additions & 1 deletion src/hooks/useOverlay/OverlayController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ export const OverlayController = forwardRef(function OverlayController(
) {
const [isOpen, setIsOpen] = useState(false);

const handleClose = useCallback(() => setIsOpen(false), []);
const handleClose = useCallback(() => {
setIsOpen(false), onExit();
}, []);

useImperativeHandle(
ref,
Expand Down
13 changes: 12 additions & 1 deletion src/hooks/useOverlay/OverlayProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { createContext, PropsWithChildren, ReactNode, useCallback, useMemo, useState } from 'react';
import React, { createContext, PropsWithChildren, ReactNode, useCallback, useEffect, useMemo, useState } from 'react';

export const OverlayContext = createContext<{
mount(id: string, element: ReactNode): void;
Expand All @@ -10,6 +10,17 @@ if (process.env.NODE_ENV !== 'production') {

export function OverlayProvider({ children }: PropsWithChildren) {
const [overlayById, setOverlayById] = useState<Map<string, ReactNode>>(new Map());
const isOpenModals = overlayById.size !== 0;

useEffect(() => {
if (isOpenModals) {
document.body.style.overflow = 'hidden';

return () => {
document.body.style.overflow = 'auto';
};
}
}, [isOpenModals]);

const mount = useCallback((id: string, element: ReactNode) => {
setOverlayById(overlayById => {
Expand Down

0 comments on commit fe13016

Please sign in to comment.