一、什么是React createPortal?
React createPortal是React 16版本中新增的API,它可以将组件渲染到组件层级之外的DOM节点上,解决了在组件内部渲染弹框、悬浮框等组件时,DOM节点层次结构不合理的问题。该API的使用方式如下:
{`ReactDOM.createPortal(child, container)`}
其中child参数代表要渲染的React元素或组件,container参数代表一个有效的DOM容器元素。
二、React createPortal与ReactDOM.render()的区别
React createPortal实际上与ReactDOM.render()非常类似,它们的共同点是都可以将React元素渲染到容器元素内。但是它们的不同点在于渲染的位置。ReactDOM.render()只能将元素渲染至组件的容器内部,而React createPortal可以将元素渲染到组件层级之外的任意节点上。
换言之,ReactDOM.render()是在作用于内部渲染,而React createPortal是在作用于外部渲染,它可以绕过控制台的警告提示,例如控制台可能会对style样式的使用进行警告,但使用React createPortal可以忽略这些警告。
三、React createPortal与组件上下文的关系
React createPortal渲染的内容是在组件外部渲染的,但是其中嵌套的组件可以与组件上下文交互。在createPortal的情况下,组件的上下文可以被update并且componentWillUnmount正确地被触发。
例如:
{`class ParentComponent extends React.Component {
state = {
showChildComponent: false
}
render() {
return (
{this.state.showChildComponent && (
)}
)
}
}
// 在Modal内部使用createPortal
class Modal extends React.Component {
constructor(props) {
super(props)
this.container = document.createElement('div')
document.body.appendChild(this.container)
}
componentWillUnmount() {
document.body.removeChild(this.container)
}
render() {
return ReactDOM.createPortal(
this.props.children,
this.container
)
}
}
class ChildComponent extends React.Component {
componentWillUnmount() {
console.log('componentWillUnmount')
}
render() {
return (
Child Component
)
}
}`}
当点击button时,Modal组件会使用createPortal将ChildComponent渲染至document.body下,这个时候ChildComponent会触发componentDidUpdate。当Modal组件被卸载时,ChildComponent会触发componentWillUnmount。
四、React createPortal的应用场景
React createPortal在以下几个场景中(但不仅限于此)被广泛应用:
1. 在Modal中使用
{`class Modal extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement('div');
this.modalRoot = document.getElementById('modal-root');
}
componentDidMount() {
this.modalRoot.appendChild(this.el);
}
componentWillUnmount() {
this.modalRoot.removeChild(this.el);
}
render() {
return ReactDOM.createPortal(
this.props.children,
this.el,
);
}
}`}
2. 在悬浮框、下拉菜单等组件中使用
{`class Dropdown extends React.Component {
constructor(props) {
super(props);
this.dropdown = document.createElement('div');
this.dropdown.classList.add('dropdown');
this.state = { isOpen: false };
}
componentDidMount() {
document.body.appendChild(this.dropdown);
}
componentWillUnmount() {
document.body.removeChild(this.dropdown);
}
toggleDropdown = () => {
this.setState(state => ({ isOpen: !state.isOpen }));
};
render() {
const { isOpen } = this.state;
const dropdownContent = (
{this.props.children}
);
return (
{this.props.trigger}
{ReactDOM.createPortal(
dropdownContent,
this.dropdown,
)}
);
}
}`}
以上代码实现了一个下拉菜单的组件,通过使用ReactDOM.createPortal,将下拉菜单组件渲染到固定的DOM节点上,实现了悬浮框元素不受外部DOM层级的限制。
五、结尾
React createPortal是一个非常强大的API,它解决了在组件内部渲染弹框、悬浮框等组件时,DOM节点层次结构不合理的问题。同时,使用React createPortal也可以提高组件的可复用性和可维护性。因此,在实际开发中,我们应该结合具体场景,合理运用React createPortal。