1. Motivate
- 키오스크의 모양을 만들기 위해 최상단에 바로 UI들의 최상위 컴포넌트가 존재하지 않고 한번 감싸져 있는 형태.
- 모달을 띄우는 경우 오른쪽 사진의 빨간 영역안에 띄우고 싶었음(키오스크의 디스플레이를 가정)
- 즉, 모달을 호출하는 부분은 최상위에서 한참 내려온 하위 컴포넌트이지만, 렌더되는 곳은 훨씬 위쪽.
- state로 관리하고 있다가 띄우는것은 비효율적으로 느껴짐. props drilling이 일어나야하거나, modal만을 위해서 context api로 관리하는 전역상태가 또 생겨야함.
- 그래서 react의 createPortal을 사용하기로 함.

2. 문제
- react 공식문서를 비롯한 다른 reference는 index.html에 app div와 나란히 최상위에 modal-root를 만들고, 직접 querySelector로 찾아서 createPortal을 하는 방식이었음
- 이 방식은 직접 dom을 찾아야한다는 점에서 원하는 곳에 마음대로 넣기에는 ㄱ제약이 있었음
- 원하는 컴포넌트를 동적으로 portal root로 삼고 싶었음!
3. 해결
- useRef를 이용해 원하는 컴포넌트의 HTMLElement를 portalRoot로 삼았음.
- 다만, 포탈을 만들고자하는 곳에서 원하는 컴포넌트의 ref를 접근하기 위해 PortalStore를 만들어 둠.
import { createPortal } from 'react-dom';
const portalStore = (() => {
let portalRoot: HTMLElement;
function setPortalRoot(newPortalRoot: HTMLElement) {
portalRoot = newPortalRoot;
}
function makePortal() {
return ({ children }: { children: React.ReactNode }) =>
createPortal(children, portalRoot);
}
return { makePortal, setPortalRoot };
})();
export default portalStore;
- 포탈로 삼고 싶은 컴포넌트가 있는 곳에서 setPortalRoot함수를 이용해 미리 포탈의 위치를 지정해두고 포탈을 사용하는 곳에서는 makePortal을 이용해 Portal Component를 얻어냄.
- 현재는 포탈 루트를 하나만 등록할 수 있지만, 원한다면 객체 형태로 많은 포탈 루트를 등록하고 사용할 수도 있음.