React如何将组件渲染到指定DOM节点详解


Posted in Javascript onSeptember 08, 2017

前言

众所周知React优点之一就是他的API特别简单。通过render 方法返回一个组件的基本结构,如同一个简单的函数,就可以得到一个可以复用的react组件。但是有时候还是会有些限制的,尤其是他的API中,不能控制组件所应该渲染到的DOM节点,这就让一些弹层组件很难控制。当父元素设置为overflow:hidden 的时候,问题就会出现了。

例如就像下面的这样:

React如何将组件渲染到指定DOM节点详解

我们实际期待的效果是这样的:

React如何将组件渲染到指定DOM节点详解

幸运的是,虽然不是很明显,但有一个相当优雅的方式来绕过这个问题。我们学到的第一个react函数是render 方法,他的函数签名是这样的:

ReactComponent render(
 ReactElement element,
 DOMElement container,
 [function callback]
)

通常情况下我们使用该方法将整个应用渲染到一个DOM节点中。好消息是该方法并不仅仅局限于此。我们可以在一个组件中,使用ReactDom.render 方法将另一个组件渲染到一个指定的DOM 元素中。作为一个组件的render 方法,其必须是纯净的(例如:不能改变state或者与DOM交互).所以我们需要在componentDidUpdate 或者 componentDidMount 中调用ReactDom.render 方法。

另外我们需要确保在父元素被卸载的时候,改组件也要被卸载掉.

整理下,我们得到下面的一个组件:

import React,{Component} from 'react';
import ReactDom from 'react-dom';
export default class RenderInBody extends Component{
 constructor(p){
  super();
 }
 componentDidMount(){//新建一个div标签并塞进body
  this.popup = document.createElement("div");
  document.body.appendChild(this.popup);
  this._renderLayer();
 }
 componentDidUpdate() {
  this._renderLayer();
 }
 componentWillUnmount(){//在组件卸载的时候,保证弹层也被卸载掉
  ReactDom.unmountComponentAtNode(this.popup);
  document.body.removeChild(this.popup);
 }
 _renderLayer(){//将弹层渲染到body下的div标签
  ReactDom.render(this.props.children, this.popup);
 }
 render(){
  return null;
 }
}

总结下就是:

在componentDidMount的时候手动向body内塞一个div标签,然后使用ReactDom.render 将组件渲染到这个div标签

当我们想把组件直接渲染到body上的时候,只需要在该组件的外面包一层RenderInBody 就可以了.

export default class Dialog extends Component{
 render(){
  return {
   <RenderInBody>i am a dialog render to body</RenderInBody>
  }
 }
}

译者增加:

将以上组件改造一下,我们就可以向指定的dom节点中渲染和卸载组件,并加上位置控制,如下:

//此组件用于在body内渲染弹层
import React,{Component} from 'react'
import ReactDom from 'react-dom';
export default class RenderInBody extends Component{
 constructor(p){
  super(p);
 }
 componentDidMount(){
  /**
  popupInfo={
   rootDom:***,//接收弹层组件的DOM节点,如document.body
   left:***,//相对位置
   top:***//位置信息
  }
  */
  let {popupInfo} = this.props; 
  this.popup = document.createElement('div');
  this.rootDom = popupInfo.rootDom;  
  this.rootDom.appendChild(this.popup);
  //we can setAttribute of the div only in this way
  this.popup.style.position='absolute';
  this.popup.style.left=popupInfo.left+'px';
  this.popup.style.top=popupInfo.top+'px';
  this._renderLayer()
 }
 componentDidUpdate() {
  this._renderLayer();
 }
 componentWillUnmount(){
  this.rootDom.removeChild(this.popup);
 }
 _renderLayer(){
  ReactDom.render(this.props.children, this.popup);
 }
 render(){
  return null;
 }
}

注:位置获取和根结点判断函数

export default (dom,classFilters)=> {
 let left = dom.offsetLeft,
  top = dom.offsetTop + dom.scrollTop,
  current = dom.offsetParent,
  rootDom = accessBodyElement(dom);//默认是body
 while (current !=null ) {
  left += current.offsetLeft;
  top += current.offsetTop;
  current = current.offsetParent;
  if (current && current.matches(classFilters)) {
   rootDom = current;
   break;
  }
 }
 return { left: left, top: top ,rootDom:rootDom};
}
/***
1. dom:为响应弹层的dom节点,或者到该dom的位置后,可以做位置的微调,让弹层位置更佳合适
*
2. classFilters:需要接收弹层组件的DOM节点的筛选类名
/

原文地址

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
Prototype使用指南之enumerable.js
Jan 10 Javascript
jquery.tmpl JQuery模板插件
Oct 10 Javascript
6款经典实用的jQuery小插件及源码(对话框/提示工具等等)
Feb 04 Javascript
深入理解JavaScript系列(33):设计模式之策略模式详解
Mar 03 Javascript
jQuery实现统计输入文字个数的方法
Mar 11 Javascript
jquery实现无限分级横向导航菜单的方法
Mar 12 Javascript
js老生常谈之this,constructor ,prototype全面解析
Apr 05 Javascript
深入理解JavaScript中的浮点数
May 18 Javascript
JS实现的按钮点击颜色切换功能示例
Oct 19 Javascript
3种vue路由传参的基本模式
Feb 22 Javascript
vue el-tree 默认展开第一个节点的实现代码
May 15 Javascript
JS如何寻找数组中心索引过程解析
Jun 01 Javascript
javascript获取指定区间范围随机数的方法
Sep 08 #Javascript
原生js实现简单的模态框示例
Sep 08 #Javascript
javascript 面向对象实战思想分享
Sep 07 #Javascript
vue 封装自定义组件之tabal列表编辑单元格组件实例代码
Sep 07 #Javascript
用js实现before和after伪类的样式修改的示例代码
Sep 07 #Javascript
vue使用drag与drop实现拖拽的示例代码
Sep 07 #Javascript
SelectPage v2.4 发布新增纯下拉列表和关闭分页功能
Sep 07 #Javascript
You might like
PHP的分页功能
2007/03/21 PHP
php下保存远程图片到本地的办法
2010/08/08 PHP
解析二进制流接口应用实例 pack、unpack、ord 函数使用方法
2013/06/18 PHP
php判断两个浮点数是否相等的方法
2015/03/14 PHP
PHP实现的进度条效果详解
2016/05/03 PHP
Mac系统完美安装PHP7详细教程
2017/06/06 PHP
对laravel的session获取与存取方法详解
2019/10/08 PHP
PHP优化之批量操作MySQL实例分析
2020/04/23 PHP
jquery动画1.加载指示器
2012/08/24 Javascript
JS保存和删除cookie操作 判断cookie是否存在
2013/11/13 Javascript
js判断浏览器类型为ie6时不执行
2014/06/15 Javascript
jQuery图片切换插件jquery.cycle.js使用示例
2014/06/16 Javascript
用JavaScript实现页面重定向功能的教程
2015/06/04 Javascript
JS实现选项卡实例详解
2015/11/17 Javascript
Vue2路由动画效果的实现代码
2017/07/10 Javascript
js实现本地时间同步功能
2017/08/26 Javascript
js的函数的按值传递参数(实例讲解)
2017/11/16 Javascript
Vue 中的compile操作方法
2018/02/26 Javascript
详解封装基础的angular4的request请求方法
2018/06/05 Javascript
jquery实现加载更多&quot;转圈圈&quot;效果(示例代码)
2020/11/09 jQuery
Python 的AES加密与解密实现
2019/07/09 Python
python爬虫项目设置一个中断重连的程序的实现
2019/07/26 Python
Django上线部署之IIS的配置方法
2019/08/22 Python
python 画图 图例自由定义方式
2020/04/17 Python
python统计mysql数据量变化并调用接口告警的示例代码
2020/09/21 Python
EMU Australia澳大利亚官网:澳大利亚本土雪地靴品牌
2019/07/24 全球购物
分家协议书
2014/04/21 职场文书
艺术设计专业毕业生推荐信
2014/07/08 职场文书
委托证明模板
2014/09/16 职场文书
交通事故委托书范本精选
2014/10/04 职场文书
行政执法队伍作风整顿个人剖析材料
2014/10/11 职场文书
学生考试舞弊检讨书
2015/01/01 职场文书
2015学校图书管理员工作总结
2015/05/11 职场文书
在职证明书模板
2015/06/15 职场文书
2016圣诞节贺卡寄语
2015/12/07 职场文书
怎样做好公众演讲能力?
2019/08/28 职场文书