본문 바로가기

FrontEnd

React Portal과 타입스크립트로 모달 만들기

반응형

원문 : https://blog.logrocket.com/build-modal-with-react-portals/

 

Using React Portals to build a modal | LogRocket Blog

Learn how to build a modal or overlay component in React with React Portals. Our demo is applicable for all top level components.

blog.logrocket.com

해당 연구 : 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)가 필요합니다.
  1. content: 모든 유효한 렌더링 가능한 React 요소
  2. 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에서 제거되도록 해야 합니다.

또한 정리 프로세스 중에 기존 컴포넌트를 제거하지 않아야 합니다.


완료된 모습

본문을 읽는것보다 코드를 한번 보는게 이해가 쉽습니다.

 

반응형