React.js处理组件对应的DOM需要在父组件以外的地方渲染的情况

对于悬浮窗一类的组件,组件里的内容往往需要渲染在父组件以外的地方,比如<body>,此时组件内需要做如下操作:

  1. 在组件对应的Class中准备一个变量container,默认值为null,用于存储渲染容器对应的DOM,该变量将用于从<body>中删除该DOM的操作。
  2. 组件默认的render()函数返回null,防止该组件在父组件中渲染DOM。
  3. 在组件的componentDidUpdate()生命周期中处理相关的渲染逻辑。如果渲染用的容器不存在或被删除,则手工使用原生的document.createElement()创建对应的容器,并使用document.body.appendChild(),将该容器添加到<body>的末尾,然后使用this.container变量存储渲染用容器DOM;最后使用ReactDOM.render()(第2个参数传渲染容器对应的DOM)将待渲染的内容渲染到容器DOM中。
  4. 在组件的componentWillUnmount()进行对应的销毁操作,比如当挂在<body>上的渲染用的容器DOM仍然存在时,将该DOM从<body>中删除。

这样可以防止因弹出框不在<body>上而导致出现悬浮窗被意外遮挡等问题。

注意

范例

以下是一个简单的对话框组件的代码:

import {Component} from 'react';
import ReactDOM from 'react-dom';
export default class Dialog extends Component{
  //Modal容器对应的DOM
  container=null;
  componentDidUpdate(){
    let visible=this.props.visible;
    //切换body上的容器的新建和销毁
    if(!visible && this.container){
      document.body.removeChild(this.container);
      this.container=null;
    }else if(visible && !this.container){
      let container = document.createElement("div");
      container.classList.add('dialog-container');
      if(this.props.modalClass){
        container.classList.add(this.props.modalClass);
      }
      this.container=container;
      document.body.appendChild(container);
    }
    //更新对话框里的内容
    if(!visible){
      return;
    }
    let classList=['dialog-body'];
    if(this.props.customClass){
      classList.push(this.props.customClass);
    }
    ReactDOM.render(
      <div className={classList.join(' ')}>{this.props.children}</div>,
      this.container
    );
  }
  componentWillUnmount(){
    //Modal被销毁时,将对应的DOM手动清除
    if(this.container){
      document.body.removeChild(this.container);
      this.container=null;
    }
  }
  //不使用组件默认的render()函数,因为该组件对应的DOM不在父组件对应的DOM下面,而在body上
  render(){
    return null;
  }
}