createPortal(children, domNode) render children vào một DOM node khác — thoát khỏi parent về mặt DOM. Use case: modal, tooltip, dropdown cần thoát overflow: hidden hoặc context z-index của ancestor.
Điểm "ảo diệu": dù DOM tách rời, event vẫn bubble theo React tree, không theo DOM tree.
jsx
import { createPortal } from 'react-dom'
function Modal({ children, onClose }) {
return createPortal(
<div className="overlay" onClick={onClose}>
<div className="modal-content">{children}</div>
</div>,
document.getElementById('modal-root') // DOM node khác
)
}
function App() {
return (
<div onClick={() => console.log('App caught click')}>
<Modal>
<button>Click me</button>
</Modal>
</div>
)
}
// → Click button trong modal VẪN log "App caught click"
// dù trong DOM, button nằm trong #modal-root, không nằm trong AppHệ quả:
- ✅ Context, hooks, refs hoạt động bình thường qua portal (cùng React tree).
- ✅ Parent component bắt được event từ portal child — tiện cho modal close-on-overlay-click.
- ⚠️ Nếu cần chặn bubble lên parent, gọi e.stopPropagation() trong portal.
- ⚠️ Đừng nhầm: cấu trúc DOM thay đổi → CSS selector .parent .child không match nữa.