반응형
원문 : https://blog.logrocket.com/build-modal-with-react-portals/
해당 연구 : https://sumo.com/stories/pop-up-statistics 에 따르면 모달은 사용자의 이목을 끄는데 효과적입니다.
하지만 모달은 만들기 어렵습니다.
z-index, 레이어, 돔 계층 추적은 쉽지 않습니다.
따라서 오버레이나 툴팁도 만들기 어렵습니다.
React 앱에서 컴포넌트 혹은 엘리먼트는 가장 가까운 부모 노드의 자식으로 DOM에 마운트됩니다.
위에서 아래로의 표준 레이어 계층 구조는 다음과 같습니다.
root node => parent nodes => child nodes => leaf nodes
상위 노드의 overflow 속성이 hidden으로 설정되어 있거나
상위 노드의 상위 레이어 엘리먼트가 있는 경우,
하위 노드는 상위 레이어에 나타나지 않고,
하위 노드가 나타날 수 있는 범위는 상위 노드의 가시 영역으로 제한됩니다.
자식을 최상위 계층으로 가져오기 위해 매우 높은 z-index 값을 설정할 수 있지만
이 전략은 지루할 수 있고 항상 성공적인 것은 아닙니다.
여기가 React Portal이 유리한 곳입니다.
React Portal은 컴포넌트 간의 부모-자식 관계를 손상시키지 않고,
기본 계층 외부에서 엘리먼트를 렌더링할 수 있는 기능을 제공합니다.
만들 컴포넌트
- ReactPortal: Portal을 생성하고 기본 계층 외부에 제공된 컨테이너의 콘텐츠를 렌더링하는 래퍼 컴포넌트.
- Modal: ReactPortal을 사용하여 렌더링할 JSX 콘텐츠가 있는 모달 컴포넌트.
- App : Modal 컴포넌트를 사용하고 활성 상태(열림 또는 닫힘)를 유지하는 위치.
리액트 포탈 만들기
react-dom에서 가져온 createPortal 함수를 사용하여 React Portal을 만들 수 있습니다. 두 가지 인수(arguments)가 필요합니다.
- content: 모든 유효한 렌더링 가능한 React 요소
- containerElement: 콘텐츠를 추가할 수 있는 유효한 DOM 요소
ReactDOM.createPortal(content, containerElement);
왜 prepend가 아니라 append인가요? 왜 정적으로 미리 추가해두지 않나요?
엘리먼트에 z-index가 설정되지 않은 경우, DOM 계층 구조의 기본 동작은
계층 구조의 아래에서 나타나는 요소가 더 높은 우선 순위를 갖는다는 것입니다.
따라서 DOM에서 본문(모든 요소 뒤에)을 추가하면 포털 컨테이너 요소가 계층 구조에서 더 높은 우선 순위를 갖게 됩니다.
<body>
<div id="root" />
<div id="portal-root" />
</body>
위의 스니펫에서 ID가 portal-root인 div는 나중에 표시되기 때문에 더 높은 우선 순위를 갖습니다.
HTML을 직접 수정하거나 프로그래밍 방식으로 포탈 루트를 추가하는 것은 당신의 선택에 달려있습니다.
동적인 wrapperId
React Hooks useLayoutEffect와 useEffect는 비슷한 결과를 얻지만 사용법이 약간 다릅니다.
- useEffect는 비동기적으로 실행됩니다.
- 효과가 동기식이어야 하고 DOM에 직접적인 변형이 있는 경우 useLayoutEffect를 사용합니다.
- 이것은 매우 드물기 때문에 일반적으로 useEffect가 가장 좋은 옵션입니다.
DOM을 직접 변경하고 DOM이 다시 칠해지기 전에 효과가 동기적으로 실행되기를 원하므로,
useLayoutEffect Hook을 사용하는 것이 더 합리적입니다.
클린업 함수
요소를 찾을 수 없는 경우 DOM을 직접 변경하고 본문에 빈 div를 추가합니다.
따라서 ReactPortal 컴포넌트가 마운트 해제될 때 동적으로 추가된 빈 div가 DOM에서 제거되도록 해야 합니다.
또한 정리 프로세스 중에 기존 컴포넌트를 제거하지 않아야 합니다.
완료된 모습
본문을 읽는것보다 코드를 한번 보는게 이해가 쉽습니다.
반응형
'FrontEnd' 카테고리의 다른 글
The Difference Between VAC(View Asset Component) Pattern and Container/Presenter Pattern (0) | 2022.06.25 |
---|---|
리액트 개발자를 위한 CSS Variable (0) | 2022.06.24 |
Intersection Observer와 React.lazy로 성능 개선하기 (0) | 2022.06.21 |
리액트 쿼리 : 폼 (2) | 2022.06.20 |
리액트 쿼리 : 뮤테이션 (0) | 2022.06.20 |