치춘짱베리굿나이스

react-portal 사용해보기 본문

ClientSide/React

react-portal 사용해보기

치춘 2022. 5. 15. 17:50

react-portal

부모 컴포턴트의 바깥에 있는 DOM 노드에 자식을 렌더링할 수 있는 기능이다

쉽게 말하면! 리액트에서 모든 컴포넌트는 root에 렌더링이 되는데, react-portal을 이용하면 root 밖에 있는 요소에도 컴포넌트를 렌더링할 수 있다는 것이다

이렇게... modal 태그는 root의 밖에 있음에도 마치 root 안에 있는 것처럼 연결해주는 것이다

마치 게임에 나오는 포탈처럼 말이다

react-portal 써보기

1. index.html 파일의 root 태그 바깥에 요소 끼워넣기

...
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <div id="modal"></div>
  </body>
</html>

리액트는 root 요소 안에만 요소를 렌더링함을 기억하고, 완전 바깥에 modal 요소를 넣어보자

이렇게 html 파일 안에 요소를 끼워넣으면, 요소가 존재하지 않는 경우가 없으므로 document.getElementByID() 를 통해 요소를 가져와도 오류가 나지 않는다

정 찝찝하면 useRef써도 상관 없다

2. document.getElementByID로 요소 가져오기

import ReactDom from 'react-dom';

interface IPortalType {
  children: React.ReactNode;
}

const ModalPortal = ({ children }: IPortalType): React.ReactPortal => {
  const element = document.getElementById('modal') as Element;
  return ReactDom.createPortal(children, element);
};

export default modalPortal;

document.getElementById() 를 통해 아까 body 태그에 끼워넣었던 modal 요소를 element로 가져오자

element로 향하는 포탈을 생성할 것이다

ReactDom.createPortal 함수는 포탈건과 비슷한 역할을 하며, 첫 번째 인자 children을 두 번째 인자 element 안에 렌더링할 것이다

오늘만큼은 우리가 첼이다 부모 컴포넌트에 파란색 포탈, 자식 컴포넌트에 주황색 포탈을 열어보자

3. 자식으로 넣을 컴포넌트 만들기

export const Modal = ({ setIsHidden }: IModalType) => {
  return (
    <ModalPortal>
      <div className={styles.modalBackground}>
        <div className={styles.modalContainer}>
          <header>컨텐츠1</header>
          <main>컨텐츠2</main>
        </div>
      </div>
    </ModalPortal>
  );
};

간단하게 모달 컴포넌트를 작성한다

포탈이 걸린 요소 (element) 의 자식으로 넣어줄 컴포넌트는 앞에서 작성했던 ModalPortal 함수로 감싸준다

진짜 모달처럼 보일 수 있도록 백그라운드도 그려주었다

모달 열림 / 닫힘 여부를 위해 상태값 설정하는 함수도 부모에게서 넘겨줘야 하지만 자세한 설명은 일단 생략하였다

4. Portal로 부모 - 자식 간 컴포넌트 연결

const [isModalOpen, setIsModalOpen] = useState(false);
...
return (
    ...
    <ModalPortal>
        {isModalOpen && (
            <Modal
                isFavorite={isFavorite}
                setIsFavorite={setIsFavorite}
                setIsModalOpen={setIsModalOpen}
                movieData={movieData}
            />
        )}
    </ModalPortal>
...
)

모달을 렌더링하고 싶은 부모 컴포넌트에 ModalPortal으로 감싼 자식 모달 컴포넌트를 심는다

ModalPortal 두 개가 포탈처럼 연결되어 자식 컴포넌트를 ModalPortal에서 정의한 태그에 그려줄 것이다

모달의 열림 / 닫힘 여부를 판정하기 위해 isModalOpen 상태값을 사용하였고, true일 때만 모달을 열리게 한 뒤 모달을 닫기 위해 props로 setIsModalOpen 함수를 넘겨주었다

이렇게 해서 isModalOpen의 값은 모달을 열고 닫는 버튼을 눌렀을때 핸들러 함수 내에서 변경된다

모달 안에서 버튼을 눌렀을 때 좋아요 여부도 바꾸기 위해 isFavorite, setIsFavorite 상태값도 넘겨준다

모달을 열었을 때 root div 하위의 modal div에서 모달을 렌더링하는 것을 볼 수 있다

결론

맨날 귀찮아서 모달 라이브러리 설치해다가 썼었는데, 기본 제공 기능인 Portal을 사용하니 생각보다 되게 쉽게 모달을 만들 수 있었고, 디자인 수정도 굉장히 쉬웠다

앞으로 굳이 라이브러리 설치 안해도 될 것 같다

참고자료

React | Portal을 이용한 Modal 구현

 

React | Portal을 이용한 Modal 구현

한국인에게 portal이란.. 닥터스트레인지의 마법진같은 포탈만 떠오를 수 있다. 이미 충분히 익숙한 다른 곳으로 이동시킨다는 개념을 이어받아, 컴포넌트를 부모 컴포넌트의 바깥에 렌더링해

dev-bomdong.tistory.com

 

'ClientSide > React' 카테고리의 다른 글

React  (0) 2022.10.01
useClickOutside 직접 구현하기  (0) 2022.07.01
데이터 불러오기, Suspense  (0) 2022.05.13
IntersectionObserver + 무한스크롤  (0) 2022.05.12
Custom Hook  (0) 2022.05.09
Comments