您的位置:

React createPortal详解

一、什么是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。