[TIL] styled-component Modal

이현동·2023년 2월 16일
0

TIL

목록 보기
33/59
post-custom-banner

Modal

지난 글에서는 Portal을 이용해서 모달창을 만들어보았는데, 문제점이 있었다. 바로 모달창이 띄워지고 나서도 뒤에 있는 contents가 scroll 된다는 점과 외부를 클릭 시에 모달창이 닫히는 기능을 추가하고 싶었다.

스크롤 막기

...

  useEffect(() => {
    const $body = document.querySelector('body');
    const overflow = $body.style.overflow;
    $body.style.overflow = 'hidden'; // body를 hidden 으로 변경
    // modal 컴포넌트가 사라졌을 때 body를 다시 스크롤 가능하게 만들어주도록 클린업 사용
    return () => {
      $body.style.overflow = overflow;
    };
  }, []);

...

useEffect를 사용해서 모달창이 렌더링 됐을 때에만 코드가 실행되고 clean up을 통해서 모달창이 닫히면 다시 스크롤 할 수 있게 하였다. document.querySelector('body')를 통해 body태그로 접근하고 body태그의 스타일에 overflow: hidden을 적용해준다. 그리고 모달창이 닫히면 다시 overflow를 원래 상태로 돌린다.

외부 클릭시 닫히기

참고 블로그에서는 커스텀훅을 통해서 기능을 구현했는데, 나중에 드롭다운 박스를 만들 때에도 외부를 통해 닫을 수 있고, 팝업창, 사이드바 등에 사용할 수 있을 것 같아 커스텀 훅으로 한번 만들어보면 재사용하기 좋을 것 같았다.

hook > useOutSideClick.jsx

import { useEffect } from 'react';

function useOutSideClick(ref, callback) {
  useEffect(() => {
    const handleClick = (event) => {
      if (ref.current && !ref.current.contains(event.target)) {
        callback();
      }
    };

    window.addEventListener('mousedown', handleClick);

    return () => window.removeEventListener('mousedown', handleClick);
  }, [ref, callback]);
}

export default useOutSideClick;

window.addEventListener를 통해 클릭을 감지해서 클릭한 html요소를 ref로 보내주게 된다. 이때, ref로 지정한 효소 밖을 클릭 하게 되면 callback함수를 실행해서 모달을 닫아준다.

components > ModalBox.jsx

import useOutSideClick from '../../hook/useOutSideClick';

...

  const modalRef = useRef(null);
  const [isOpen, setIsOpen] = useState(false);
  const onClickButton = () => {
    setIsOpen(true);
  };
  
  useOutSideClick(modalRef, onClose);
  
  ...
function ModalBox({ children, onClose, confirm }) {
    
    ...
    
  return (
    ...
    <ModalWrap>
      <Modal ref={modalRef}> {/* 이 요소 밖을 클릭 시 callback함수는 onClose 실행 */  }
        <ModalText>{children}</ModalText>
        <BtnWrapper>
          <Btn onClick={() => setIsOpen(false)} smBtn danger>
            닫기
          </Btn>
          {confirm && <Btn smBtn>확인</Btn>}
        </BtnWrapper>
      </Modal>
    </ModalWrap>
    ...
)
    ...

참고자료

react portal을 사용한 모달창 만들기, 하루 기록

profile
https://hdlee.dev
post-custom-banner

0개의 댓글