React - Portals를 통해 모달 렌더링 하기(with. Next.js)

BigbrotherShin·2020년 5월 25일
1

Frontend

목록 보기
30/31
post-thumbnail

Portals 는 리액트 프로젝트에서 컴포넌트를 렌더링하게 될 때, UI 를 어디에 렌더링 시킬지 DOM 을 사전에 선택하여 부모 컴포넌트의 바깥에 렌더링 할 수 있게 해주는 기능입니다. Portals를 사용하면 DOM의 계층구조 시스템에 종속되지 않으면서 컴포넌트를 렌더링 할 수 있습니다.

출처: 리액트 - Portals 를 통한 부모 컴포넌트의 외부 DOM 에 컴포넌트 렌더링하기

기본 설정으로 Next.js는 id 값이 __next인 DOM에 MyApp 컴포넌트를 렌더링합니다.
우리는 React Portals를 사용하여 __nextDOM이 아닌 다른 계층의 DOM에 모달을 렌더링해야하므로 _document.js 파일의 <Main /> 하단에 <div id='modal'></div>를 추가해줍니다.

pages/_document.js

import Document, { Head, Main, NextScript } from 'next/document';


export default class MyDocument extends Document {
  ...

  render() {
    return (
      <html>
        <Head>
          <meta charSet='utf-8' />
          <meta
            name='viewport'
            content='initial-scale=1.0, width=device-width'
          />
          {this.props.styles}
        </Head>
        <body>
          <Main /> // MyApp 컴포넌트가 렌더링되는 DOM
          <div id='modal'></div> // Modal을 렌더링 할 새로운 DOM하나를 만들어줍니다.
          <NextScript />
        </body>
      </html>
    );
  }
}

ModalPortal 만들기

components/ModalPortal.js

import ReactDOM from 'react-dom';
import { memo } from 'react';

const ModalPortal = ({ children }) => {
  const el = document.getElementById('modal');
  return ReactDOM.createPortal(children, el);
};

export default memo(ModalPortal);

Modal 감싸기

모달을 적용하고 싶은 컴포넌트를 위에서 만든 ModalPortal 컴포넌트로 감싸줍니다.

components/SetModal.js

import React from 'react;

const SetModal = () => {
  return (
   {onModal ? (
    <ModalPortal>
      <Modal>
	    <FollowList followData={followData} />
      </Modal>
    </ModalPortal>
      ) : null}
  )
}

export default SetModal;

Tip: 모달이 떴을 때 스크롤 고정하기

모달을 스크롤 되는 페이지에 추가했을 때, 모달이 떠있음에도 불구하고 스크롤이 동작한다면 다음과 같이 해주면 된다.

components/Modal.js

  useEffect(() => {
    // 모달이 떴을 때 뒤의 영역 스크롤 고정
    document.body.style.cssText = `position: fixed; top: -${window.scrollY}px`;
    return () => {
      const scrollY = document.body.style.top;
      document.body.style.cssText = `position: ""; top: "";`;
      window.scrollTo(0, parseInt(scrollY || '0') * -1);
    };
  }, []);

cssText를 쓰는 이유는 style을 여러번 접근하면 그 횟수만큼 reflow가 발생하게 됩니다. cssText를 이용하면 1번만 계산하기 때문에 이렇게 js로 css를 건드릴 경우 퍼포먼스를 위해 필수로 해주시는게 좋습니다. (class 명을 추가해줘도 됩니다.)

출처: 효율적인 리액트 모달(react-modal) 만들기 - bestsup - Medium

References

profile
JavaScript, Node.js 그리고 React를 배우는

0개의 댓글