diff --git a/June/article/How-to-handle-multiple-modals-in-a-React-application.md b/June/article/How-to-handle-multiple-modals-in-a-React-application.md new file mode 100644 index 0000000..abf4912 --- /dev/null +++ b/June/article/How-to-handle-multiple-modals-in-a-React-application.md @@ -0,0 +1,278 @@ +## πŸ”— [How to handle multiple modals in a React application](https://dev.to/zettadam/how-to-handle-multiple-modals-in-a-react-application-2pei) + +### πŸ—“οΈ λ²ˆμ—­ λ‚ μ§œ: 2024.06.29 + +### 🧚 λ²ˆμ—­ν•œ 크루: 러기(λ°•μ •μš°) + +--- + +## λ¦¬μ•‘νŠΈ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ μ—¬λŸ¬κ°œμ˜ λͺ¨λ‹¬λ“€μ„ κ΄€λ¦¬ν•˜λŠ” 방법 + +μ°Έκ³ : 전체 예제 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ μ—¬κΈ°μ—μ„œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€: https://stackblitz.com/edit/react-modals + +React μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ λͺ¨λ‹¬μ„ κ΄€λ¦¬ν•˜λŠ” 방법은 ν•˜λ‚˜λ§Œ μžˆλŠ” 것이 μ•„λ‹ˆμ§€λ§Œ, λͺ‡λͺ‡ 방식이 λ‹€λ₯Έ 방식보닀 λ‚˜μ„ 수 μžˆμŠ΅λ‹ˆλ‹€. 이 κΈ€μ—μ„œλŠ” Redux μŠ€ν† μ–΄ 같은 κΈ€λ‘œλ²Œ μŠ€ν† μ–΄λ₯Ό μ‚¬μš©ν•˜μ—¬ λͺ¨λ‹¬μ„ κ΄€λ¦¬ν•˜λŠ” 것보닀 더 κ°„λ‹¨ν•œ 방법을 μ†Œκ°œν•˜κ³ μž ν•©λ‹ˆλ‹€. 이 μ˜ˆμ œμ—μ„œλŠ” μ»΄ν¬λ„ŒνŠΈ μƒνƒœμ™€ 이벀트 버블링을 μ‚¬μš©ν•  것이며, μ΄λŠ” React의 Portals도 μ–ΈκΈ‰λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. + +λͺ¨λ‹¬μ€ 보톡 λΌμš°ν„°μ— μ˜ν•΄ κ΄€λ¦¬λ˜λŠ” λ³„λ„μ˜ ν™”λ©΄κ³Ό λΉ„μŠ·ν•œ 뢀뢄이 μžˆμŠ΅λ‹ˆλ‹€. + +## AppShell + +예λ₯Ό λ“€μ–΄ λ‹€μŒ src/AppShell.jsx μ˜ˆμ œμ™€ 같이, 두 μ’…λ₯˜μ˜ μ»΄ν¬λ„ŒνŠΈλ₯Ό 쀑앙 μ»΄ν¬λ„ŒνŠΈμ—μ„œ μ„œλ‘œ κ°€κΉκ²Œ λ Œλ”λ§ν•˜λŠ” 것이 합리적일 수 μžˆμŠ΅λ‹ˆλ‹€. + +```tsx +import React, { useState } from 'react' +import { BrowserRouter, NavLink, Route, Switch } from 'react-router-dom' + +import ScreenOne from './components/screen-one/ScreenOne' +import ScreenTwo from './components/screen-two/ScreenTwo' +import ScreenThree from './components/screen-three/ScreenThree' + +import ModalOne from './components/common/modal-one/ModalOne' +import ModalTwo from './components/common/modal-two/ModalTwo' +import ModalThree from './components/common/modal-three/ModalThree' + +import './app-shell.css' + +const AppShell = () => { + const [modalOpen, setModal] = useState(false) + + const openModal = event => { + event.preventDefault() + const { target: { dataset: { modal }}} = event + if (modal) setModal(modal) + } + + const closeModal = () => { + setModal('') + } + + return ( + +
+ + {/* Application header and navigation */} +
+

React Modal Windows

+ +
+ + {/* Application screens */} + + + + + + + + + + + + + + + + {/* Modals */} + + + + + + + {/* Application footer */} + + +
+
+``` + +## 단일 μ±…μž„ μ»΄ν¬λ„ŒνŠΈλ‘œ λ¦¬νŒ©ν„°λ§ ν•˜κΈ° + +λ§Œμ•½ μ—¬λŸ¬λΆ„μ˜ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ λ§Žμ€ ν™”λ©΄ ν˜Ήμ€ λ§Žμ€ λͺ¨λ‹¬μ„ ν¬ν•¨ν•˜κ³  μžˆλ‹€λ©΄, 예λ₯Ό λ“€μ–΄ ScreenSwitchboard.jsx와 ModalManager.jsx와 같이 λΌμš°νŠΈμ™€ λͺ¨λ‹¬μ„ λ³„λ„μ˜ μ»΄ν¬λ„ŒνŠΈλ‘œ μΆ”μΆœν•¨μœΌλ‘œμ¨ AppShell.jsx μ»΄ν¬λ„ŒνŠΈλ₯Ό μ’€ 더 κΉ”λ”ν•˜κ²Œ λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€. + +```tsx +import React, { useState } from "react"; +import { BrowserRouter } from "react-router-dom"; + +import AppHeader from "./AppHeader"; +import AppFooter from "./AppFooter"; + +import ScreenSwitchboard from "./ScreenSwitchboard"; +import ModalManager from "./ModalManager"; + +import "./app-shell.css"; + +const AppShell = () => { + const [modalOpen, setModal] = useState(false); + + const openModal = (event) => { + event.preventDefault(); + const { + target: { + dataset: { modal }, + }, + } = event; + if (modal) setModal(modal); + }; + + const closeModal = () => { + setModal(""); + }; + + return ( + +
+ + + + +
+
+ ); +}; + +export default AppShell; +``` + +## 이벀트 버블링을 μ‚¬μš©ν•˜μ—¬ νŠΉμ • λͺ¨λ‹¬ μ—΄κΈ° + +μš°λ¦¬λŠ” #app--shell μš”μ†Œμ—μ„œ λ²„λΈ”λ§λœ 클릭 이벀트λ₯Ό μΊ‘μ²˜ν•©λ‹ˆλ‹€. νŠΉμ • λͺ¨λ‹¬μ„ 열도둝 νŠΈλ¦¬κ±°ν•˜λŠ” 이벀트 ν•Έλ“€λŸ¬ openModal은 우리 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ 일뢀 μš”μ†Œ(λ²„νŠΌ, 링크 λ“±)에 μ„€μ •ν•  수 μžˆλŠ” data-modal 속성을 μ°ΎμŠ΅λ‹ˆλ‹€. + +μ•„λž˜λŠ” ν΄λ¦­ν–ˆμ„ λ•Œ λͺ¨λ‹¬μ„ 열도둝 νŠΈλ¦¬κ±°ν•˜λŠ” λ²„νŠΌμ΄ μžˆλŠ” ν™”λ©΄ μ»΄ν¬λ„ŒνŠΈμ˜ μ˜ˆμ‹œμž…λ‹ˆλ‹€. + +```tsx +import React from "react"; + +const ScreenOne = ({}) => { + return ( +
+

Screen One

+ +
+ + + +
+
+ ); +}; + +export default ScreenOne; +``` + +λ³΄μ‹œλ‹€μ‹œν”Ό, μš°λ¦¬λŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ 계측 ꡬ쑰 props 내리기λ₯Ό 톡해 ν•¨μˆ˜λ‚˜ 값을 μ „λ‹¬ν•˜κ³  μžˆμ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λŒ€μ‹ , data-modal 속성과 이벀트 버블링에 μ˜μ‘΄ν•˜μ—¬ νŠΉμ • λͺ¨λ‹¬μ„ μ—¬λŠ” 것을 μ²˜λ¦¬ν•©λ‹ˆλ‹€. + +## ModalManager + +우리의 μ»΄ν¬λ„ŒνŠΈλŠ” 두 가지 propsλ₯Ό μš”κ΅¬ν•©λ‹ˆλ‹€: μ–΄λ–€ λͺ¨λ‹¬μ΄ μ—΄λ €μ•Ό ν•˜λŠ”μ§€λ₯Ό μ„€λͺ…ν•˜λŠ” modal propμœΌλ‘œμ„œμ˜ μƒνƒœ κ°’κ³Ό, μ—΄λ € μžˆλŠ” λͺ¨λ‹¬μ„ 닫도둝 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ 효과적으둜 μ§€μ‹œν•˜λŠ” closeFn propμž…λ‹ˆλ‹€. + +μ°Έκ³ : λͺ¨λ‹¬μ€ κ°„λ‹¨ν•œ μ½˜ν…μΈ λ₯Ό 포함할 μˆ˜λ„ 있고, 폼 μ²˜λ¦¬μ™€ 같은 더 λ³΅μž‘ν•œ 경우λ₯Ό λ‹€λ£° μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. μš°λ¦¬λŠ” κ·Έλ“€μ˜ λ‹«νž˜μ„ μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄ 클릭 이벀트 버블링에 μ˜μ‘΄ν•˜κ³  싢지 μ•ŠμŠ΅λ‹ˆλ‹€. μ—¬κΈ°μ—μ„œ prop을 μ‚¬μš©ν•˜λŠ” 것이 더 κ°„λ‹¨ν•˜κ³  μœ μ—°ν•©λ‹ˆλ‹€. + +λ‹€μŒμ€ μ»΄ν¬λ„ŒνŠΈμž…λ‹ˆλ‹€. + +```tsx +import React from "react"; + +import ModalOne from "./components/common/modal-one/ModalOne"; +import ModalTwo from "./components/common/modal-two/ModalTwo"; +import ModalThree from "./components/common/modal-three/ModalThree"; + +const ModalManager = ({ closeFn = () => null, modal = "" }) => ( + <> + + + + + + +); + +export default ModalManager; +``` + +## React Portal을 μ‚¬μš©ν•˜μ—¬ λͺ¨λ‹¬ λ Œλ”λ§ν•˜κΈ° + +ν•œ λ²ˆμ— ν•˜λ‚˜μ˜ Modal만 ν‘œμ‹œν•˜λŠ” 것이 κ°€μž₯ 일반적인 νŒ¨ν„΄μ΄λ―€λ‘œ, μžμ‹ μš”μ†Œλ₯Ό React ν¬ν„Έλ‘œ λ Œλ”λ§ν•  래퍼 μ»΄ν¬λ„ŒνŠΈλ₯Ό λ§Œλ“œλŠ” 것이 νƒ€λ‹Ήν•˜λ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€. + +λ‹€μŒμ€ src/components/common/modal/Modal.jsx μ»΄ν¬λ„ŒνŠΈμ˜ μ½”λ“œμž…λ‹ˆλ‹€. + +```tsx +import React, { useEffect } from "react"; +import ReactDOM from "react-dom"; + +const modalRootEl = document.getElementById("modal-root"); + +const Modal = ({ children, open = false }) => { + if (!open) return null; + + return ReactDOM.createPortal(children, modalRootEl); +}; + +export default Modal; +``` + +μš°λ¦¬λŠ” #modal-root μš”μ†Œκ°€ document μ–΄λ”˜κ°€μ—, 특히 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ 마운트된 #app-root μš”μ†Œμ˜ ν˜•μ œ μš”μ†Œλ‘œμ„œ μ‘΄μž¬ν•˜κΈ°λ₯Ό κΈ°λŒ€ν•©λ‹ˆλ‹€. + +예λ₯Ό λ“€μ–΄, index.html의 λŠ” λ‹€μŒκ³Ό 같이 보일 수 μžˆμŠ΅λ‹ˆλ‹€: + +```html + +
+ + +``` + +λ§ˆμ§€λ§‰μœΌλ‘œ, νŠΉμ • λͺ¨λ‹¬ μ»΄ν¬λ„ŒνŠΈμ˜ μ˜ˆμ‹œμž…λ‹ˆλ‹€. + +```tsx +import React from "react"; + +import Modal from "../modal/Modal"; + +const ModalOne = ({ closeFn = () => null, open = false }) => { + return ( + +
+
+
+

Modal One

+
+
+

Modal One content will be rendered here.

+
+ +
+
+
+ ); +}; + +export default ModalOne; +``` + +이 κΈ€μ—μ„œλŠ” ꡬ체적인 μ˜ˆμ‹œλ₯Ό λ“€λ©΄μ„œ μƒλŒ€μ μœΌλ‘œ 짧고 κ°„λ‹¨ν•˜κ²Œ λ§Œλ“€κ³ μž λͺ¨λ“  λ‚΄μš©μ„ 닀루지 μ•Šμ•˜μŠ΅λ‹ˆλ‹€. μŠ€νƒ€μΌλ§, μ ‘κ·Όμ„± 그리고 μ•„λ§ˆλ„ λ‹€λ₯Έ μš”μ†Œλ“€μ„ κ³ λ €ν•΄μ•Ό ν•  κ²ƒμž…λ‹ˆλ‹€. + +이 κΈ€μ˜ 맨 μœ„μ— κ²Œμ‹œλœ λ§ν¬μ—μ„œ 이 μ†ŒμŠ€ μ½”λ“œλ₯Ό 찾을 수 μžˆμŠ΅λ‹ˆλ‹€. + +λŒ“κΈ€λ‘œ 이 글에 λŒ€ν•΄ μ–΄λ–»κ²Œ μƒκ°ν•˜λŠ”μ§€, ν˜Ήμ€ μ—¬λŸ¬λΆ„μ˜ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ λͺ¨λ‹¬μ„ μ–΄λ–»κ²Œ κ΄€λ¦¬ν•˜κ³  μžˆλŠ”μ§€ μ•Œλ €μ£Όμ„Έμš”.