React.createPortal(child, container)
React.createPortal
를 사용하여 portal을 생성함자식 컴포넌트가 부모 컴포넌트의 container에서 시각적으로 벗어나야 할 경우 주로 사용됨
overflow: hidden
과 z-index
같은 CSS property가 필요함Potal's use cases
portal을 사용할 때, 키보드 focus 관리
가 매우 중요함
class Modal extends React.Component {
constructor(props) {
super(props);
}
render() {
return ReactDOM.createPortal(
this.props.children,
domNode
);
}
}
const Modal = ({ message, isOpen, onClose, children }) => {
if (!isOpen) return null;
return ReactDOM.createPortal(
<div className="modal">
<span className="message">{message}</span>
<button onClick={onClose}>Close</button>
</div>,
domNode);
}
<html>
<body>
<div id="app-root"></div>
<div id="modal-root"></div>
</body>
</html>
// appRoot와 modalRoot은 DOM에서 형제 관계임
const appRoot = document.getElementById('app-root');
const modalRoot = document.getElementById('modal-root');
class Modal extends React.Componet {
constructor(props) {
super(props);
this.el = document.createElement('div');
}
componentDidMount() {
// Portal 요소는 Modal의 자식이 마운트된 후 DOM 트리에 삽입됨
// 자식은 어디에도 연결되지 않은 DOM 노드로 마운트 됨
// 자식 컴포넌트가 마운트될 때 그것을 즉시 DOM 트리에 연결해야 한다면 (DOM 노드 계산, 자식 노트에서 'autoFocus' 사용),
// Modal에 state를 추가하고 Modal이 DOM 트리에 삽입되어 있을 때만 자식을 렌더링하기
modalRoot.appendChild(this.el);
}
componentWillUnmount() {
modalRoot.removeChild(this.el);
}
render() {
return ReactDOM.createPortal(
this.props.children,
this.el
);
}
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { clicks: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// Child 컴포넌트 내에 있는 버튼이 클릭했을 때, 그 버튼이 DOM 상 직계 자식이 아니더라도 이벤트 버블링에 의해 이벤트가 전파되어 Parent의 state를 갱신함
this.setState(state => ({
clicks: state.clicks + 1
}));
}
render() {
return (
<div onClick={this.handleClick}>
<p>Number of clicks: {this.state.clicks}</p>
</div>
<Modal>
<Child />
</Modal>
)
}
}
이벤트 버블링이 일어남
React가 portal 노드와 그 생명주기를 제어함
portal은 DOM 구조에만 영향을 미침
HTML 마운트 지점을 미리 정의해야 함