포털
- Published on
- 리액트의 `createPortal`을 사용하여 DOM의 다른 부분에 모달과 같은 UI 요소를 렌더링하는 방법과 관련 예시를 설명합니다.
Table of Contents
ReactDOM.createPortal
사용하기:
const modalRoot = document.getElementById("modal-root") as HTMLElement;
// html 파일에 'modal-root' id를 가진 div가 있다고 가정합니다;
export class Modal extends React.Component<{ children?: React.ReactNode }> {
el: HTMLElement = document.createElement("div");
componentDidMount() {
modalRoot.appendChild(this.el);
}
componentWillUnmount() {
modalRoot.removeChild(this.el);
}
render() {
return ReactDOM.createPortal(this.props.children, this.el);
}
}
훅을 사용하는 방법
위와 동일하지만 훅을 사용하여 구현한 예시입니다.
import { useEffect, useRef, ReactNode } from "react";
import { createPortal } from "react-dom";
const modalRoot = document.querySelector("#modal-root") as HTMLElement;
type ModalProps = {
children: ReactNode;
};
function Modal({ children }: ModalProps) {
// ref를 사용해 div 요소를 한 번만 생성합니다
const elRef = useRef<HTMLDivElement | null>(null);
if (!elRef.current) elRef.current = document.createElement("div");
useEffect(() => {
const el = elRef.current!; // 절대 null이 아니기 때문에 non-null 단언 사용
modalRoot.appendChild(el);
return () => {
modalRoot.removeChild(el);
};
}, []);
return createPortal(children, elRef.current);
}
모달 컴포넌트 사용 예시:
import { useState } from "react";
function App() {
const [showModal, setShowModal] = useState(false);
return (
<div>
// 정적 html 파일에 이 부분을 넣을 수도 있습니다
<div id="modal-root"></div>
{showModal && (
<Modal>
<div
style={{
display: "grid",
placeItems: "center",
height: "100vh",
width: "100vh",
background: "rgba(0,0,0,0.1)",
zIndex: 99,
}}
>
모달입니다!{" "}
<button
style={{ background: "papyawhip" }}
onClick={() => setShowModal(false)}
>
닫기
</button>
</div>
</Modal>
)}
<button onClick={() => setShowModal(true)}>모달 보기</button>
// 앱의 나머지 부분
</div>
);
}
예시의 배경
이 예시는 React 문서의 포털을 통한 이벤트 버블링 예시를 바탕으로 합니다.