public/index.html
<div id="root"></div>
index.html을 보면 root라는 id를 가진 div 태그가 있다.
이 div태그는 root DOM을 생성하고, src 폴더 내부의 react 코드를 추가해서 DOM Tree를 만든다.
src/index.js
ReactDOM.render(<App />, document.getElementById('root'));
src/index.js 파일에서 위의 코드로 App.js에서 작성한 코드를 root DOM에 넣어주는 것이다.
리액트에선 App 파일내부에 모든 코드를 넣기 때문에 App 컴포넌트가 전체의 부모가 된다.
근데 이 부모 컴포넌트로 부터 종속되지 않는 컴포넌트를 만들어야 할 때가 있다.
예를 들어 모달창이다.
그럴때 react portal을 이용해서 부모 컴포넌트 외부에 렌더링 시켜 분리된 컴포넌트를 개발 할 수 있다.
react 홈페이지에서는 아래와 같이 설명한다.
Portal은 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링하는 최고의 방법을 제공합니다.
ReactDOM.createPortal(child, container)
사용법은 위 코드로 사용하고 매개변수는 아래와 같다.
📌 이 말은 child에 분리하고 싶은 컴포넌트를 넣고 container에 부모로 삼고 싶은 DOM을 넣어주면 된다는 말이다.
public/index.html
<div id="root"></div>
<div id="modal"></div>
src/ModalPortal.js
import ReactDOM from 'react-dom';
const ModalPortal = ({ children }) => {
const el = document.getElementById('modal');
return ReactDOM.createPortal(children, el);
};
export default ModalPortal;
📌 index.html의 modal DOM에 children(reactNode) 코드를 넣는다는 느낌이다.
src/MyModal.js
import React from "react";
import "./MyModal.css";
const MyModal = ({ onClose }) => {
return (
<div className="MyModal">
<div className="content">
<h3>모달</h3>
<p>여기는 modal Dom</p>
<button onClick={onClose}>Close</button>
</div>
</div>
);
};
export default MyModal;
👆 ModalPortal children안에 넣을 modal 컴포넌트 이다.
src/App.js
import React, { Component, useState } from "react";
import MyModal from "./MyModal";
import ModalPortal from "./ModalPortal";
import "./App.css";
const App = () => {
const [modal, setModal] = useState(false);
const handleOpenModal = () => {
setModal(true);
};
const handleCloseModal = () => {
setModal(false);
};
return (
<div className="App">
<h1>여기는 root Dom</h1>
<button onClick={handleOpenModal}>Open</button>
{modal && (
<ModalPortal>
<MyModal onClose={handleCloseModal} />
</ModalPortal>
)}{" "}
</div>
);
};
export default App;
❗️ App 안에서 ModalPortal로 children 컴포넌트 묶어주면 이 컴포넌트는 modal DOM에 종속된 요소이다.
css
App.css
.App {
text-align: center;
color: #61dafb;
}
MyModal.css
.MyModal {
background: rgba(0, 0, 0, 0.25);
position: fixed;
left: 0;
top: 0;
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.MyModal .content {
background: white;
padding: 1rem;
width: 400px;
height: auto;
}
위 css 까지 적용해 주고 실행해 보면 아래처럼 잘 나온다
❓ 근데 이걸거면 걍 display:none 해놨다가 꺼내면 되는거 아닌가 라는 생각이 들었지만 css를 보면 다른 DOM이기 때문에 글자색이랑 가운데 정렬이 들어가지 않고 정해준 css 만 들어간걸 볼 수 있다.
👍 모달만들땐 이방식으로 만드는게 정답인것 같다.
참고: https://ko.reactjs.org/docs/portals.html
https://velog.io/@velopert/react-portals