๊ฐ์ธ ํ๋ก์ ํธ๋ก ์งํํ ํฌ์ผ๋ชฌ ๋๊ฐ ์ค modal ์์์ ๋ฌธ์ ์ ์ ๋ฐ๊ฒฌํ๊ฒ ๋์๋ค.
ํด๋น ์คํฌ๋ฆฐ์ท ์ฒ๋ผ modal์ด ํด๋น ์์ ๋ด๋ถ์ ์กด์ฌํ๊ณ ์๋ค.
๋ฌธ์ ๊ฐ ์์ ์๋ ์์ง๋ง ์ด๋ด ๊ฒฝ์ฐ ๋ถ๋ชจ ์์์ ๋ฐ๋ผ ์ํฅ์ ๋ฐ๊ฒ ๋๋ค.
๋ด๊ฐ ์ํ๋ ๋ฐฉํฅ์ modal ๋ฑ์ ์์๋ ์ต์์์ ์์นํ๋๊ฒ ๋ง๋ค๊ณ ์๊ฐํ๋ค.
body์์ ์ต์๋จ ๋๋ ์ตํ๋จ์ ๋ฐฐ์นํ๊ฑฐ๋ root์ ๋ฐฐ์นํ๊ณ ์ถ๋ค.
๊ทธ๋ฌ๋ค ์๊ฒ ๋๊ฒ create portal
์ด๋ค.
ํด๋น ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด modal์ ๋ฆฌํฉํ ๋งํ ์์ ์ด๋ค.
react create portal sample
์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ ์์น๋ฅผ DOM ํธ๋ฆฌ์ ๋ค๋ฅธ ๊ณณ์ผ๋ก ์ง์ ํ ์ ์๋๋ก ํด์ค๋ค.
์ฃผ๋ก Modal, Tooltip, BottomSheet ๋ฑ ํน์ํ ์์์ ์ฌ์ฉํ๋ค.
createPortal(children, domNode, key?)
children
: React๋ก ๋ ๋๋งํ ์ ์๋ ๋ชจ๋ ๊ฒ, JSX, Fragment, ๋ฌธ์์ด์ด๋ ์ซ์ ๋๋ ์ด๋ฌํ ์์๋ค์ ๋ฐฐ์ด (ex. <div />
, <Component />
๋ฑ)
domNode
: DOM ๋
ธ๋๊ฐ ์์ด์ผ ํ ์์น (ex. document.body
๋ฑ)
key(์ ํ)
: portal ํค๋ก ์ฌ์ฉ๋ ๊ณ ์ ํ ๋ฌธ์์ด์ด๋ ์ซ์
์ฉ๋ฒ
- DOM์ ๋ค๋ฅธ ๋ถ๋ถ์ ๋ ๋๋ง
- Portal์ ์ฌ์ฉํด ๋ชจ๋ฌ ๋ํ ์์ ๋ ๋๋ง
- React ๊ตฌ์ฑ ์์๋ฅผ React๊ฐ ์๋ DOM ๋ ธ๋๋ก ๋ ๋๋ง
createPortal - react docs
root ์๋จ์ rootPortal์ ์ถ๊ฐํด ์คฌ๋ค.
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/nav_logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PokeDex</title>
</head>
<body>
<div id="rootPortal"></div>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
portal์ modal ๋ง๊ณ ๋ ๋ค๋ฅธ ์์์์๋ ์ฌ์ฉํ ์ ์๋๋ก
๋
๋ฆฝ์ ์ธ ์ปดํฌ๋ํธ๋ก ์์ฑํด ์คฌ๋ค.
import { ReactNode } from 'react';
import { createPortal } from 'react-dom';
const Portal = ({ children }: { children: ReactNode }) => {
const rootPortal = document.getElementById('rootPortal') as HTMLElement;
return createPortal(children, rootPortal);
};
export default Portal;
์ด์ ์ฝ๋์์ Modal ์ปดํฌ๋ํธ๋ฅผ ๋ฐ๋ก ๋ง๋ค์ง ์์์ง๋ง
์ด๋ฒ์ ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฆฌํด ์์
ํด ์คฌ๋ค.
๋ค์ ๋ฐฐ๊ฒฝ์ด ํ์์ ์ผ๋ก ๋ค์ด๊ฐ๊ณ
ํด๋ฆญ ์ ๋ซํ์ผํ๊ธฐ์ DropBox์ actionFn์ด๋ผ๋ prop์ ์ถ๊ฐํ๋ค.
import styled from 'styled-components';
import DropBox from '../box/DropBox';
import Portal from '../portal';
import { ReactNode } from 'react';
const Modal = ({ children, actionFn }: { children: ReactNode; actionFn: () => void }) => {
return (
<Portal>
<ModalContainer>
<ModalWrapper>{children}</ModalWrapper>
<DropBox actionFn={actionFn} />
</ModalContainer>
</Portal>
);
};
export default Modal;
๋ชจ๋ฌ๋ก ํ์๋๊ธธ ์ํ๋ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ธ์ฃผ๋ฉด ๋
{modal && nameId === name && (
<Modal actionFn={() => dispatch(overlayMadalActions.toggleModal())}>
<PokemonDetail name={name} />
</Modal>
)}
์นด๋ ์ปดํฌ๋ํธ ํด๋ฆญ ์ rootPortal๋ก ์์๊ฐ ์์ฑ๋๋๊ฑธ ํ์ธํ ์ ์๋ค.
createPortal์ ๋ํด ๊ธ๋ก๋ง ๋๋ต ์๊ณ ์์๋๋ฐ ์ด๋ ๊ฒ ์ ์ฉ ํ ์ ์๋ ๊ธฐํ์๋ค.
์ด์ ์ ํญ์ ํด๋น ์์ ๊ทผ์ฒ์ ํ์ ๋๋๋ก ํ๋๋ฐ
ํ์ตํ๋ ๊ธฐ์ต๊ณผ ์ง๊ธ ์งํํ๊ณ ์๋ ์ฌ์ด๋ ํ๋ก์ ํธ๋ฅผ ํตํด
createPortal๋ก modal, tooltip ๋ฑ์ ๊ด๋ฆฌํ๋ ๊ฑธ ์๊ฒ ๋์๋ค.
(๋๋ฌด ๋ฆ๊ฒ ์์ ๋ฌธ์ ...)
์ง๊ธ์ ๊ฐ๋จํ๊ฒ ๋์ํ๋ ๋ฐฉ์๋ง ์ตํ๋ ์ ๋๋ก
ํ ์ด ํ๋ก์ ํธ ์ฝ๋๋ฅผ ๋ฆฌํฉํ ๋ง ํ๋ค.
modal ์ฝ๋๋ฅผ ์์ ํ๋ฉด์ ์์ ์ ๋ด๊ฐ ์ค์ํ ์ฝ๋๋ ๊ฐ์ด ์์ ํ ์ ์๋ ๊ณ๊ธฐ๊ฐ ๋์๋ค.
๋์ค์ index.html์ div์์๋ฅผ ์ถ๊ฐํ๋ ๋ฐฉ์์ด ์๋
๋์ ์ผ๋ก ์์ฑ๋๊ณ ์ ๊ฑฐ ๋๋๋ก ๋ฆฌํฉํ ๋ง ํ ์์ ์ด๋ค.
๋ํ id ๊ฐ๋ ์ญํ ์ ๋ง๊ฒ ๋ณ๊ฒฝ๋๋๋ก ํ ์์ ์ด๋ค. (์ง๊ธ์.. ์ง์ค๋ ฅ ํ๋ฝ..)
๋ฆฌํฉํ ๋งํ ๊ฒฝ์ฐ ํด๋น ๋ด์ฉ๋ ์์ ์์
+) ๊ฐ๋ง์ ํ ์ด ํ๋ก์ ํธ ์ฝ๋๋ฅผ ๋ณด๋ ๋ฆฌํฉํ ๋งํ ๋ถ๋ถ์ด ๋ง์๋ค ๋ค์ ์๊ฐ ๋ ๋ ํํ์ด ์์ ํด ๋ณด์.