Portal은 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링하는 최고의 방법을 제공합니다.
ReactDOM.createPortal(child, container)
첫 번째 인자(child
)는 엘리먼트, 문자열 혹은 fragment와 같은 어떤 종류이든 렌더링할 수 있는 React 자식입니다. 두 번째 인자(container)는 DOM 엘리먼트입니다.
// Example.jsx
import React from 'react';
import ThankyouDialog from './ThankyouDialog';
export default function Example() {
return (
<div>
<ThankyouDialog />
<div style={{ position: "absolute"}}
><button>하하하</button></div>
</div>
)
}
// Dialog.jsx
import React, { useState } from 'react'
export default function Dialog(props) {
const [isOpen, setIsOpen] = useState(false)
return (
<>
<button onClick={() => setIsOpen(true)}>Open</button>
{isOpen && (<div
style={{
position: "absolute",
zIndex: 99,
top: "50%",
left: "50%",
transform: "translate(-50%,-50%)",
border: "1px solid black",
padding: 24,
backgroundColor: "white",
}}
>
{typeof props.title === "string" ? (<h1>{props.title}</h1>) : (props.title)}
<h5>{props.description}</h5>
<button style={{ backgroundColor: "red", color: "white" }} onClick={() => setIsOpen(false)}>{props.button}</button>
</div>
)}
{isOpen && <div style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'lightgray',
}}/>}
</>
)
}
// ThankyouDialog.jsx
import React from 'react'
import Dialog from './Dialog'
export default function ThankyouDialog() {
return (
<Dialog title={<h1 style={{color: "gold"}}>Thanks</h1>} description="It is honor to meet you!" button="close"/>
)
}
Dialog를 composition 한 ThankyouDialog는 인라인 속성으로 z-index 속성을 99를 주었다.
근데 하하하
버튼이 가려지지 않고 노출되고 있다.
// Example.jsx
import React from 'react';
import ThankyouDialog from './ThankyouDialog';
export default function Example() {
return (
<div>
<div style={{ position: "absolute"}}
><button>하하하</button></div>
<ThankyouDialog />
</div>
)
}
위 코드처럼 ThankyouDialog를 하하하 button
밑으로 배치해야 의도했던 대로 버튼이 가려진다.
이럴 때 Portal
이 필요
1️⃣ index.html에 id가 root인 div 밑에 <div id="portal"></div>
코드 추가
<!--index.html-->
<div id="root"></div>
<div id="portal"></div> <!-- 추가 -->
2️⃣ Portal Component 생성하고 대상을 Portal로 감싼다.
// Example.jsx
import React from "react";
import { createPortal } from "react-dom";
import ThankyouDialog from "./ThankyouDialog";
const Portal = (props) => {
return createPortal(props.children, document.getElementById("portal"));
};
export default function Example() {
return (
<div>
<Portal>
<ThankyouDialog />
</Portal>
<div style={{ position: "absolute" }}>
<button>하하하</button>
</div>
</div>
);
}
이제 하하하 button
이 가려진다 !!
Portal 은 root의 영역이 아니라서 아래 코드처럼 div에 onClick 이벤트로 'div'를 console창에 출력하는 작업을 넣고 ThankyouDialog를 클릭하면 출력이 안될 것 같다.
하지만, React는 Portal에 있는 요소라고 하더라도 이 안에서 일어나는 이벤트를 root에 같이 전달해준다... 💡
// Example.jsx
import React from "react";
import { createPortal } from "react-dom";
import ThankyouDialog from "./ThankyouDialog";
const Portal = (props) => {
return createPortal(props.children, document.getElementById("portal"));
};
export default function Example() {
return (
<div onClick={() => console.log("div")}>
<Portal>
<ThankyouDialog />
</Portal>
<div style={{ position: "absolute" }}>
<button>하하하</button>
</div>
</div>
);
}
- createPortal => 부모 컴포넌트 DOM 트리로부터 벗어나기
- 이벤트 => Portal에 있더라도 Event는 트리로 전파