React-Portal로 Modal 구현하기

2024. 11. 27. 17:05·⚛️ React

일반적으로 react는 부모 컴포넌트가 렌더링되면 자식 컴포넌트가 렌더링되는 tree 구조를 가지고 있다.

하지만 때때로 이런 tree구조가 불편함을 가져다주기도 해서, 

이럴 때 부모-자식 관계를 유지하지만 독립적인 위치에서 렌더링을 하면 훨씬 편리한 경우가 있다.

 

 

대표적인 예로 modal은 부모 컴포넌트의 스타일링 속성에 제약을 받아 z-index 등으로 번거로운 후처리를 해줘야한다.

이러한 상황에서 portal을 통해 독립적인 구조와 부모-자식 관계를 동시에 유지할 수 있다면, z-index 등 부모 컴포넌트의 제약에서 벗어날 수 있다.

 

1. portal로 modal 구현하기

2. dim영역 클릭시 모달 닫기

3, 브라우저 뒤로가기 방지-> 이벤트 처리 

 

1. portal로 modal 구현하기 

 

Portal은 컴포넌트 트리와 별개로 특정 DOM 노드에 React 컴포넌트를 렌더링하는 기능을 제공합니다. 이를 통해 모달과 같은 컴포넌트를 root DOM 트리 외부에서 관리할 수 있습니다.

 


const Modal: React.FC<ModalProps> = ({ isOpen, title, onClose, children }) => {
    if (!isOpen) return null;
   
    useEffect(() => {
        const preventGoBack = () => {
            history.go(1);
            onClose();
        };

        history.pushState(null, "", location.href);
        window.addEventListener("popstate", preventGoBack);

        return () => window.removeEventListener("popstate", preventGoBack);
    }, [onClose]);

    return createPortal(
        <ModalOverlay>
            <ModalContainer>
                <ModalBox>
                    <ModalHeader>
                        <h2>{title}</h2>
                        <CloseButton onClick={onClose}>&times;</CloseButton>
                    </ModalHeader>
                    <ModalContent>{children}</ModalContent>
                    <ButtonContainer>
                        <Button onClick={onClose}>취소</Button>
                        <Button>확인</Button>
                    </ButtonContainer>
                </ModalBox>
            </ModalContainer>
        </ModalOverlay>,
        document.getElementById("modal")!,
    );
};

export default Modal;

 

 

 

  • createPortal:
    • createPortal(children, container)는 두 가지 인수를 받습니다:
      1. children: 렌더링할 React 노드 (모달 UI 구성).
      2. container: React 컴포넌트를 렌더링할 DOM 노드. 여기서는 document.getElementById("modal")에 렌더링됩니다.
    • 모달이 DOM 트리의 다른 곳(modal ID가 있는 컨테이너)에서 렌더링됩니다.
  • document.getElementById("modal"):
    • Portal의 렌더링 위치입니다.
    • public/index.html에 <div id="modal"></div>를 추가함

 

Portal을 통해 렌더링:

  • 모달은 document.getElementById("modal") 노드에 렌더링됩니다.
  • 이를 통해 모달이 부모 컴포넌트의 스타일에 영향을 받지 않고 독립적으로 동작합니다

 

2. dim 클릭으로 모달 닫기 

 
 <ModalOverlay onClick={onClose}>
            <ModalContainer onClick={(e) => e.stopPropagation()}>
                <ModalBox>
                    <ModalHeader>
 
 

 

  1. Dim 클릭 이벤트 (onClick):
    • ModalOverlay에 onClick={onClose}를 추가하여 Dim 영역을 클릭하면 모달을 닫습니다.
  2. 이벤트 전파 차단:
    • 모달 내부를 클릭해도 Dim 클릭 이벤트가 발생하지 않도록 ModalContainer에 onClick={(e) => e.stopPropagation()}를 추가.
    • e.stopPropagation()은 부모 요소로의 이벤트 전파를 차단합니다.
  3. 사용법:
    • onClose prop에 모달 닫기 함수(setIsModalOpen(false))를 전달하면, Dim 클릭 시 모달이 닫힙니다.

 

 

 

 

3.브라우저 뒤로가기 방지

const preventGoBack = () => {
      history.go(1);  // 브라우저를 다시 현재 페이지로 이동
      onClose();  // 모달 닫기 등의 동작 수행
    };

 

  • history.go(1):
    • 브라우저의 히스토리를 수정하여 사용자가 "뒤로가기"를 눌러도 다시 현재 페이지로 이동
    • 이렇게 하면 실제로 뒤로가기 동작이 발생하지 않도록 막을 수 있습니다.
  • onClose():
    • 뒤로가기 이벤트가 발생했을 때 모달을 닫거나 원하는 동작을 수행합니다.

    history.pushState(null, "", location.href);
 

 

history.pushState:

  • 현재 페이지의 히스토리 상태를 추가
  • 브라우저의 히스토리 스택에 현재 페이지가 추가되므로 "뒤로가기"를 눌렀을 때 다시 현재 페이지로 돌아오게 됩니다.
 
window.addEventListener("popstate", preventGoBack);
 

 

popstate 이벤트:

  • 브라우저 히스토리 상태가 변경되었을 때 발생하는 이벤트
  • 뒤로가기가 발생하면 preventGoBack 함수가 호출되어 뒤로가기를 방지하고 원하는 동작(onClose)을 수행합니다.
 
useEffect(() => {
        const preventGoBack = () => {
            history.go(1);
            onClose();
        };

        history.pushState(null, "", location.href);
        window.addEventListener("popstate", preventGoBack);

        return () => window.removeEventListener("popstate", preventGoBack);
    }, [onClose]);
 

 

 

 

 

 

https://dev-bomdong.tistory.com/21

 

 

 

 

 

'⚛️ React' 카테고리의 다른 글

firebase + redux 로 상태관리 하기  (0) 2024.12.10
드롭박스를 만들어보자.. +외부클릭으로 드롭다운 닫히는 동작  (0) 2024.11.28
[React] React-Hook-Form에 대해 알아보기  (1) 2024.11.12
Virtual DOM  (1) 2024.11.11
React - StrictMode  (0) 2024.11.11
'⚛️ React' 카테고리의 다른 글
  • firebase + redux 로 상태관리 하기
  • 드롭박스를 만들어보자.. +외부클릭으로 드롭다운 닫히는 동작
  • [React] React-Hook-Form에 대해 알아보기
  • Virtual DOM
gprorogpfus
gprorogpfus
:- >
  • gprorogpfus
    gpfusdldks
    gprorogpfus
  • 전체
    오늘
    어제
    • 분류 전체보기 (55)
      • 🎭JavaScript (2)
      • 👚 CSS (1)
      • ⚛️ React (13)
      • 🌃 Next.js (5)
        • 🔜 next.js-study (3)
      • 🥏TypeScript (10)
      • 🏴알고리즘 (2)
      • 🌴트러블슈팅 (3)
      • ⛲ 프로젝트 (6)
        • 👖gproro-shop-app (8)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 글쓰기
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Redux
    react
    JavaScript
    TypeScript
    GIT
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
gprorogpfus
React-Portal로 Modal 구현하기
상단으로

티스토리툴바