요소를 렌더링하는게 아니라 리액트에 의해 이미 렌더링 된 html 내용을 다른 곳으로 옮기는 것이다.
오버레이 내용이 있는 모달이 깊게 중첩되면 안되는 문제를 해결할 수 있다.
return (
<>
<MyModal />
<MyInputForm />
</>
)
<section>
...
<div>
<h2>A Modal</h2>
</div>
<form>
<label>Username</label>
<input type="text" />
</form>
</section>
위 코드는 동작하지만 좋지 않은게 있는데 바로 모달이다. 하지만 의미적인 관점이나 간결한 html 구조를 갖췄는지의 관점으로 보면 별로 좋지 않은 구조이다. 왜냐하면 모달은 전체 페이지 위에 표시되는 오버레이이기 때문이다.
따라서, 모달이 다른 html 코드 안에 중첩되어 있다면 기술적으로는 잘 동작할지라도 좋은 코드도 좋은 구조도 아니어서 문제가 생길 수 있다. 모달 뿐만 아니라 사이드바나 다이얼로그와 같은 일반적인 모든 종류의 오버레이나 관련 컴포넌트에서 일어날 수 있다.
이런 비효율적인 구조를 Portal을 이용해 해결할 수 있다.
포탈에는 두 가지가 필요하다.
<div id="backdrop-root"></div>
// 모든 종류의 오버레이나 모달, 사이드바 등을 가져올 수 있다.
<div id="overlay-root"></div>
<div id="root"></div>
그런다음 이동시킬 컴포넌트에서 리액트에게 해당 컴포넌트가 어딘가로 포탈되어야 한다고 알린다.
일단 띄워질 모달과 그림자를 의미하는 두 개의 컴포넌트로 나누어 작업했다.
// 그림자 컴포넌트
const Backdrop = (props) => {
return ...
}
// 모달 컴포넌트
const ModalOverlay = (props) => {
return ...
}
그런다음 .createPortal() 메서드를 사용하기 위해 react dom을 import 한다.
import ReactDOM from 'react-dom'
...
return (
<>
{ReactDOM.createPortal(<Backdrop />, document.getElementById('backdrop-root'))}
{ReactDOM.createPortal(<ModalOverlay />, document.getElementById('overlay-root'))}
</>
)
JSX코드에서 이 컴포넌트를 어디에서 사용하든 상관없이 아무리 다른 요소들에 깊숙이 둘러쌓여 있어도 포탈되는 장소에서 나타난다.
.createPortal(렌더링 되어야 하는 리액트 노드(JSX), 이 요소가 렌더링되어야 하는 실제 DOM의 컨테이너를 가리키는 포인터)