Portal
8주차 과제로 모달을 만들어보기는 했지만 너무 미숙하게 만든 감이 있어서 새로 만들어 보고 싶었다.
이번에는 모달 만들때 주로 쓰는 방식이라는 portal 을 이용해보기로 했다.
portal 을 사용하는 이유
리액트는 부모 컴포넌트가 렌더링 되면 자식 컴포넌트가 렌더링 되는 Tree 구조를 가지고 있다.
보통 최상위 컴포넌트인 #root 의 div 태그 아래에 부모 컴포넌트부터 렌더링 된다.
이런 Tree 구조는 DOM 계층 구조에서 부모-자식 간에 CSS 상속 같은 영향을 미친다.
하지만 리액트 포탈을 사용하면 독립적인 위치에서 렌더링한다.
독립적인 위치에서 렌더링 하면 부모 컴포넌트의 영향을 받지 않을 수 있다.
따라서 리액트 포탈을 사용하면 CSS 충돌을 방지하고 레이아웃 및 이벤트 처리를 효과적으로 할 수 있어
유지 보수성을 향상시킬 수 있다.
모달 구현하기
기본적인 구현 방식은 아래와 같다.
- HTML 최상위에 #modal-container 를 둔다.
- 모달을 만든 후 portal 을 이용하여 #modal-container 를 연결한다.
index.html
<!doctype html>
<html lang="ko">
<head>
<title>Open Mind</title>
</head>
<body>
<div id="root"></div>
<div id="modal-container"></div>
</body>
</html>
App.jsx
function App() {
const [ isModalOpen, setIsModalOpen ] = useState(false);
const handleOpen = () => setIsModalOpen(true);
const handleClose = () => setIsModalOpen(false);
return (
<>
{isModalOpen && (
<Modal onClose={handleClose}>
모달 내용
<button onClick={onClose}>모달 닫기</button>
</Modal>
)}
<button onClick={handleOpen}>모달 열기</button>
</>
);
}
export default App;
Modal.jsx
import { useRef } from "react";
import ReactDOM from "react-dom";
import styled from "styled-components";
function Modal(props) {
const { children, onClose } = props;
const modalRef = useRef(null);
// useRef를 이용해 모달창 바깥을 누르면 모달창 꺼지도록 함
const handleClickOutside = (e) => {
if (modalRef.current && !modalRef.current.contains(e.target)) {
onClose();
}
};
return ReactDOM.createPortal(
<ModalBg onClick={handleClickOutside}>
<ModalContainer ref={modalRef}>{children}</ModalContainer>
</ModalBg>,
document.getElementById("modal-container"),
);
}
export default Modal;
const ModalBg = styled.div`
// 모달 배경 스타일
`;
const ModalContainer = styled.div`
// 모달 컨테이너 스타일
`;
'프론트엔드 > React' 카테고리의 다른 글
react-query와 redux (0) | 2024.02.01 |
---|---|
Vite 와 CRA (0) | 2024.01.20 |
Yarn Berry와 NPM (0) | 2024.01.19 |