모달창 만들기

이경준·2021년 6월 15일
0

빵덕

목록 보기
4/7


클릭시 모달창이 나오는 기능을 구현하던중 재사용이 만을듯하여 커스텀훅으로 만들기로 하였다.
생각해야할 이슈는
1. 모달바깥쪽을 클릭했을때 모달이 사라져야한다.
2. 모달이 생겼을때 스크롤을 없애야한다.

1. html구조 파악하기

import React, { ReactElement } from 'react';

const BottomModal = (): ReactElement => {
  return (
    <S.Container >
      <S.Modal >
        <S.Title>{title}</S.Title>
        <S.Btn onClick={onLastBtnClick}>{content}</S.Btn2>
      </S.Box>
    </S.Container>
  );
};

export default BottomModal;

컴포넌트로 모달을 만들어 재사용할것이기 때문에 먼저 jsx구조와 스타일을 적용하였다.

2. 이벤트 적용하기

import React, { ReactElement, useRef, useState } from 'react';

const BottomModal = (): ReactElement => {
  const visibleContentRef = useRef<null || HTMLDivElement>(null)
  const [modalOn,setModalOn] = useState(false)
  
const clickOutSide = (e:React.MouseEvent<HTMLDicElement, MouseEvent>) => {
  if (
      e.target instanceof HTMLDivElement &&
      visibleContentRef.current &&
      !visibleContentRef.current.contains(e.target) &&
      setModalOn
    ) {
      setModalOn(false);
    }
}
  
  return (
    {modalOn 
     ? <S.Container onClick={clickOutSide}>
        <S.Modal ref={visibleContentRef}>
          <S.Title>{title}</S.Title>
          <S.Btn>{content}</S.Btn2>
        </S.Box>
      </S.Container> 
     : null 
    }
  );
};

export default BottomModal;
  1. useState로 모달이 나타탤때와 사라질때를 정한다.
  2. 바탕화면이아닌 클릭이벤트가 들어갈 모달창에 ref를 지정한다.
  3. 개장 부모태그에 onClick함수를 넣어서 current 엘리먼트가 ref를 포함하지 않을경우 모달이 닫히게 한다.

4. 커스텀훅으로 빼기

// useClickOutside.ts
import { useState, useRef, useEffect } from 'react';

export const useClickOutside = (
  initialValue: boolean,
): [
  React.MutableRefObject<HTMLDivElement | null>,
  boolean,
  React.Dispatch<React.SetStateAction<boolean>>,
  (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void,
] => {
  const visibleContentRef = useRef<null | HTMLDivElement>(null);
  const [modalOn, setModalOn] = useState<boolean>(initialValue);

  const clickOutside = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
    if (
      e.target instanceof HTMLDivElement &&
      visibleContentRef.current &&
      !visibleContentRef.current.contains(e.target) &&
      setModalOn
    ) {
      setModalOn(false);
    }
  };

  return [visibleContentRef, modalOn, setModalOn, clickOutside];
};

커스텀훅으로 만들었으니 이제 사용을 해보자

5. 사용해보기

const ThemeTemplate = (): ReactElement => {
  const [visibleContentRef, modalOn, setModalOn, clickOutside] = useClickOutside(false);

  return (
    <section>
        <div onClick={() => setSequenceOn(true)}>
          클릭
        </div>
      {/* 모달창 */}
      {
        modalOn ? 
        <BottomModal
          visibleContentRef={visibleSequenceRef}
          clickOutside={clickOutside}
          setModalState={setModalOn}
        /> : 
        null
      }
    </section>
  );
};

export default ThemeTemplate;

이제 useClickOutSide는 커스텀훅으로써 매번 사용할수 있고, modal컴포넌트또한 값을 주입해주는 방법으로 재사용이 가능해졌다.

6. 모달발생시 스크롤 없애기

//useClickOustside.ts
import { useState, useRef, useEffect } from 'react';

export const useClickOutside = (
  initialValue: boolean,
): [
  React.MutableRefObject<HTMLDivElement | null>,
  boolean,
  React.Dispatch<React.SetStateAction<boolean>>,
  (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void,
] => {
  const visibleContentRef = useRef<null | HTMLDivElement>(null);
  const [modalOn, setModalOn] = useState<boolean>(initialValue);

  useEffect(() => {
    if (modalOn) {
      document.body.style.overflowY = 'hidden';
    } else {
      document.body.style.overflowY = 'initial';
    }
  }, [modalOn]);

  const clickOutside = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
    if (
      e.target instanceof HTMLDivElement &&
      visibleContentRef.current &&
      !visibleContentRef.current.contains(e.target) &&
      setModalOn
    ) {
      setModalOn(false);
    }
  };

  return [visibleContentRef, modalOn, setModalOn, clickOutside];
};

모달의 변화를 감지하는 useEffect를 커스텀훅에 넣어서 body.style.overflowY로 on/off를 컨트롤 하였다.

profile
내가 기억하기위한 블로그

0개의 댓글