-
Notifications
You must be signed in to change notification settings - Fork 4
[Refactor] Observer pattern을 통한 전역 모달 관리 #155
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
7c54bb8
95e1f35
dbb16da
9a63791
5988b12
fac343c
da52f43
509a915
965750b
db00e1a
aae4be4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import { useEffect, useState, type ReactNode } from "react"; | ||
| import { useLocation } from "react-router-dom"; | ||
|
|
||
| import { modalStore } from "@/shared/model/store"; | ||
|
|
||
| import { Modal } from "../../shared/ui/modal/modal"; | ||
|
|
||
| interface ModalItem { | ||
| id: string; | ||
| content: ReactNode; | ||
| autoPlay?: number; | ||
| } | ||
hummingbbird marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| export const ModalProvider = () => { | ||
| const { pathname } = useLocation(); | ||
| const [modals, setModals] = useState<ModalItem[]>([]); | ||
|
|
||
| useEffect(() => { | ||
| const unsubscribe = modalStore.subscribe(setModals); | ||
| return unsubscribe; | ||
| }, []); | ||
|
Comment on lines
+16
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial 외부 store 구독은 지금 패턴은 🤖 Prompt for AI Agents |
||
|
|
||
| useEffect(() => { | ||
| modalStore.reset(); | ||
| }, [pathname]); | ||
|
|
||
|
Comment on lines
+23
to
+26
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 지금 reset 기준이
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 지금 당장은 query를 통해 url이 바뀌는 페이지에서 모달이 사용되는 경우가 없어 |
||
| return ( | ||
| <> | ||
| {modals.map((modal) => { | ||
| return ( | ||
| <Modal | ||
| key={modal.id} | ||
| isOpen={true} | ||
| autoPlay={modal.autoPlay} | ||
| onClose={() => modalStore.close(modal.id)} | ||
| > | ||
| {modal.content} | ||
| </Modal> | ||
| ); | ||
| })} | ||
| </> | ||
| ); | ||
| }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 실제로 리팩토링된 부분을 보니 바로 이해가 되네요 !!! useModal 없이 모달을 관리할 수 있게 되면서, jsx 코드가 분리되고 별도의 선언 없이 store를 사용해 모달을 열 수 있는 점도 너무 좋습니다! 모달에 들어갈 내용들을 사용처에서 입력해주는 것도 응집도 측면에서 정말 좋은 거 같아요 천재 개발자 오수빈답습니다 👍👍
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,8 @@ | ||
| import { EXPERIENCE_TYPE } from "@/shared/config/experience"; | ||
| import { parseYMD } from "@/shared/lib/format-date"; | ||
| import { modalStore } from "@/shared/model/store"; | ||
| import { ModalBasic, Tooltip } from "@/shared/ui"; | ||
| import { Button } from "@/shared/ui/button/button"; | ||
| import { useModal } from "@/shared/ui/modal/use-modal"; | ||
| import { Tag } from "@/shared/ui/tag/tag"; | ||
| import { Textfield } from "@/shared/ui/textfield/textfield"; | ||
| import { HELP_TOOLTIP_CONTENT } from "@/shared/ui/tooltip/tooltip.content"; | ||
|
|
@@ -24,9 +24,6 @@ const ExperienceViewer = () => { | |
| const { showEditDelete, onClickEdit, onClickDelete, onToggleDefault } = | ||
| useExperienceHeaderActions(); | ||
|
|
||
| const { isOpen: isDeleteModalOpen, handleModal: toggleDeleteModal } = | ||
| useModal(); | ||
|
|
||
| const startDate = current?.startAt ? parseYMD(current.startAt) : null; | ||
| const endDate = current?.endAt ? parseYMD(current.endAt) : null; | ||
|
|
||
|
|
@@ -42,6 +39,22 @@ const ExperienceViewer = () => { | |
|
|
||
| const typeLabel = current.type ? EXPERIENCE_TYPE[current.type] : "미지정"; | ||
|
|
||
| const handleOpenDeleteModal = () => { | ||
| modalStore.open( | ||
| <ModalBasic | ||
| title="이 경험을 삭제하시겠습니까?" | ||
| subTitle="작성한 내용은 즉시 제거되며, 복구할 수 없습니다." | ||
| closeText="취소" | ||
| confirmText="삭제" | ||
| onClose={() => modalStore.reset()} // 취소 시 닫기 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재 store는 store에서
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 말씀해주신대로 close(id)로 모달을 개별적으로 닫는 것이 안전한 방향이지만,
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 이해했습니다!! 말씀해주신 부분은 제가 추후 리팩토링 진행하면서 |
||
| onConfirm={() => { | ||
| onClickDelete(); // 실제 삭제 동작 | ||
| modalStore.reset(); // 모달 닫기 | ||
| }} | ||
| /> | ||
| ); | ||
|
Comment on lines
+42
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 확인창에서
🤖 Prompt for AI Agents |
||
| }; | ||
|
|
||
| return ( | ||
| <main className={s.page}> | ||
| <StickyHeader | ||
|
|
@@ -53,7 +66,7 @@ const ExperienceViewer = () => { | |
| <Button | ||
| variant="secondary" | ||
| size="small" | ||
| onClick={toggleDeleteModal} | ||
| onClick={handleOpenDeleteModal} | ||
| > | ||
| 삭제하기 | ||
| </Button> | ||
|
|
@@ -130,19 +143,6 @@ const ExperienceViewer = () => { | |
| </div> | ||
| </div> | ||
| </section> | ||
|
|
||
| <ModalBasic | ||
| title="이 경험을 삭제하시겠습니까?" | ||
| subTitle="작성한 내용은 즉시 제거되며, 복구할 수 없습니다." | ||
| closeText="취소" | ||
| confirmText="삭제" | ||
| isOpen={isDeleteModalOpen} | ||
| onClose={toggleDeleteModal} | ||
| onConfirm={() => { | ||
| toggleDeleteModal(); | ||
| onClickDelete(); | ||
| }} | ||
| /> | ||
| </main> | ||
| ); | ||
| }; | ||
|
|
||

Uh oh!
There was an error while loading. Please reload this page.