modal은 공통 컴포넌트로 많이 사용되곤한다.
여기서 portal은 무엇일까?
Portal은 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링하는 방법이다.
컴포넌트 렌더링 메서드에서 엘리먼트를 반환할 때 그 엘리먼트는 부모 노드에서 가장 가까운 자식으로 DOM에 마운트된다.
portal을 사용하는 이유는?
전역모달을 띄우는 범용적인 방법은 <App> 바로 하위에 Modal 컴포넌트를 만든 뒤 전역상태를 통해 show / hide를 관리하는 것이다.
상위단에 DOM노드를 생성했을뿐만 아니라, 단순한 알람모달의 경우에도 전역상태에 의존한다는 불합리함이 존재한다.
모달이 켜진 상태에서 업데이트 등으로 부모 엘리먼트가 리렌더링이 된다면, tree구조에 따라 자식 모달 역시 영향을 받을 수 있다.
즉, 부모 - 자식 간의 제약에서 자유로워지기 위해서 Portal을 사용한다.
import { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
type Props = {
children: React.ReactNode;
};
const Portal = ({ children }: Props) => {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
return () => setMounted(false);
}, []);
return mounted ? createPortal(children, document.querySelector('#portal') as HTMLElement) : null;
};
export default Portal;
potal을 만들기 위해서 next에서는 _document 파일에서 body태그 하위로 <div id=”portal” /> 코드를 추가해주어야한다.
modal 컴포넌트를 만들어보자
modal 컴포넌트는 portal에 의해서 dom이 새로 생성되고, 아니고를 판단하기 위해서 portal로 감싸주어야한다.
modal은 공통 컴포넌트로 사용되기 때문에, props와 children을 이용하여 재사용이 가능하도록 컴포넌트를 만들어준다.
import { MouseEventHandler } from 'react';
import styled from 'styled-components';
import Portal from './Portal';
type Props = {
children?: React.ReactNode;
title?: React.ReactNode;
onClose: MouseEventHandler<HTMLButtonElement>;
};
const Modal = ({ title, children, onClose }: Props) => {
return (
<Portal>
<ModalWrapper>
<Title>{title}</Title>
{children}
<Button onClick={onClose}>확인</Button>
</ModalWrapper>
</Portal>
);
};
const ModalWrapper = styled.div`
position: fixed;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
width: 240px;
height: auto;
background: #ffffff;
border-radius: 4px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
`;
const Title = styled.p`
font-weight: 600;
font-size: 20px;
padding: 30px 0;
`;
const Button = styled.button`
width: 100%;
border: 0;
height: 40px;
background-color: #000;
color: #fff;
margin: 10px 0;
`;
export default Modal;
나는 위와 같이 모달 컴포넌트를 만들었다.
모달이 뜨지 않았을 시, portal만 있는 것을 볼 수 있다.
modal 이 떳을 때, portal의 자식으로 모달이 뜨는것을 확인할 수 있다.
_next 컴포넌트의 자식 컴포넌트가 리렌더링 또는 변형이 되더라도, portal DOM에는 영향이 없다.
여기서 portal은 무엇일까?
Portal은 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링하는 방법이다.
컴포넌트 렌더링 메서드에서 엘리먼트를 반환할 때 그 엘리먼트는 부모 노드에서 가장 가까운 자식으로 DOM에 마운트된다.
react의 렌더링을 알아보자
<aside> 💡 React의 렌더링을 먼저 알아보자 React의 index.html을
</aside>
portal을 사용하는 이유는?
전역모달을 띄우는 범용적인 방법은 <App> 바로 하위에 Modal 컴포넌트를 만든 뒤 전역상태를 통해 show / hide를 관리하는 것이다.
상위단에 DOM노드를 생성했을뿐만 아니라, 단순한 알람모달의 경우에도 전역상태에 의존한다는 불합리함이 존재한다.
모달이 켜진 상태에서 업데이트 등으로 부모 엘리먼트가 리렌더링이 된다면, tree구조에 따라 자식 모달 역시 영향을 받을 수 있다.
즉, 부모 - 자식 간의 제약에서 자유로워지기 위해서 Portal을 사용한다.
import { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
type Props = {
children: React.ReactNode;
};
const Portal = ({ children }: Props) => {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
return () => setMounted(false);
}, []);
return mounted ? createPortal(children, document.querySelector('#portal') as HTMLElement) : null;
};
export default Portal;
potal을 만들기 위해서 next에서는 _document 파일에서 body태그 하위로 <div id=”portal” /> 코드를 추가해주어야한다.
modal 컴포넌트를 만들어보자
modal 컴포넌트는 portal에 의해서 dom이 새로 생성되고, 아니고를 판단하기 위해서 portal로 감싸주어야한다.
modal은 공통 컴포넌트로 사용되기 때문에, props와 children을 이용하여 재사용이 가능하도록 컴포넌트를 만들어준다.
import { MouseEventHandler } from 'react';
import styled from 'styled-components';
import Portal from './Portal';
type Props = {
children?: React.ReactNode;
title?: React.ReactNode;
onClose: MouseEventHandler<HTMLButtonElement>;
};
const Modal = ({ title, children, onClose }: Props) => {
return (
<Portal>
<ModalWrapper>
<Title>{title}</Title>
{children}
<Button onClick={onClose}>확인</Button>
</ModalWrapper>
</Portal>
);
};
const ModalWrapper = styled.div`
position: fixed;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
width: 240px;
height: auto;
background: #ffffff;
border-radius: 4px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
`;
const Title = styled.p`
font-weight: 600;
font-size: 20px;
padding: 30px 0;
`;
const Button = styled.button`
width: 100%;
border: 0;
height: 40px;
background-color: #000;
color: #fff;
margin: 10px 0;
`;
export default Modal;
나는 위와 같이 모달 컴포넌트를 만들었다.
모달이 뜨지 않았을 시, portal만 있는 것을 볼 수 있다.
modal 이 떳을 때, portal의 자식으로 모달이 뜨는것을 확인할 수 있다.
_next 컴포넌트의 자식 컴포넌트가 리렌더링 또는 변형이 되더라도, portal DOM에는 영향이 없다.