Portal
children을 DOM의 다른 곳(보통 <body>의 끝)에 그립니다. 제자리에 있으면 갇히게 될 clipping이나 stacking context를 빠져나가야 하는 subtree를 위한 것입니다.
import { Portal } from 'neba';
<Portal>
<div className="fixed inset-x-0 top-0 z-50">공지</div>
</Portal>;Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| children | ReactNode | — | 다른 곳에 그려질 내용 |
| container | Element | DocumentFragment | null | (() => Element | DocumentFragment | null) | document.body | children이 갈 곳. 함수는 마운트 이후에 호출되므로, props를 만들 때는 없던 element도 대상으로 삼을 수 있습니다 |
| disabled | boolean | false | portal 대신 제자리에 그립니다. 마운트 시점에 한 번만 정하세요. 뒤집으면 안의 모든 것이 remount되고 그 안에 있던 상태가 사라집니다 |
<div>의 native 속성은 wrapper로 전달됩니다.
createPortal과의 차이
두 가지를 더합니다. 첫 번째가 이 컴포넌트를 쓰는 이유입니다.
wrapper가 **neba-portal**을 답니다. portal된 subtree는 페이지가 스타일을 scope해 둔 element를 벗어나므로 scoped stylesheet가 그것을 놓칩니다. 이 class가 다시 찾는 방법입니다. 이 라이브러리의 팝업들도 이미 이 class를 달고 있고, 이 문서 사이트도 .neba-scope 바깥에 reset을 다시 적용하는 데 씁니다.
두 번째는 서버입니다. 서버에는 document가 없으므로 portal은 마운트되기 전까지 아무것도 그리지 않고, 배포되는 마크업에 portal된 subtree는 들어 있지 않습니다. 이것은 우회할 수 있는 한계가 아니라 portal의 정의에서 따라 나오는 결과입니다. 서버 HTML에 반드시 있어야 하는 것은 portal에 넣으면 안 됩니다.
예시
container
children이 갈 곳입니다. 기본값은 document.body.
함수를 넘기면 마운트 이후에 호출됩니다. React가 직접 그리는 것을 대상으로 삼는 방법입니다. () => document.getElementById('drawer')는 props를 만들던 시점에는 없던 element를 찾습니다.
<Portal container={() => document.getElementById('overlay-root')}>{children}</Portal>disabled
portal 대신 제자리에 그립니다. 이미 portal 안에 있는 subtree, 마크업이 쓰인 자리에 그대로 있기를 바라는 테스트, 닿을 만한 document.body가 없는 임베드를 위한 것입니다.
마운트 시점에 한 번만 정하세요. React에게 portal된 subtree와 제자리의 subtree는 서로 다른 child이므로, 이 값을 뒤집으면 안의 모든 것이 remount되고 그 안에 있던 것(반쯤 채운 폼, 스크롤 위치, 재생 중이던 영상)이 사라집니다. 이 컴포넌트가 우회할 수 있는 것이 아니라 React의 reconciliation입니다.