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 相关文章推荐
打开超链需要“确认”对话框的方法
Mar 08 Javascript
编写兼容IE和FireFox的脚本
May 18 Javascript
javascript中关于break,continue的特殊用法与介绍
May 24 Javascript
jquery遍历筛选数组的几种方法和遍历解析json对象
Dec 13 Javascript
引入JS文件IE6报语法错误或缺少对象问题的解决方法
Jan 09 Javascript
jquery+ajax实现注册实时验证实例详解
Dec 08 Javascript
深入理解JavaScript中为什么string可以拥有方法
May 24 Javascript
JavaScript数组的定义及数字操作技巧
Jun 06 Javascript
React Native时间转换格式工具类分享
Oct 24 Javascript
bootstrap table表格插件之服务器端分页实例代码
Sep 12 Javascript
seajs和requirejs模块化简单案例分析
Aug 26 Javascript
vue项目中使用particles实现粒子背景效果及遇到的坑(按钮没有点击响应)
Feb 11 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 和 MYSQL
2006/10/09 PHP
用PHP+java实现自动新闻滚动窗口
2006/10/09 PHP
php类中private属性继承问题分析
2012/11/01 PHP
PHP的介绍以及优势详细分析
2019/09/05 PHP
PHP数组对象与Json转换操作实例分析
2019/10/22 PHP
对xmlHttp对象的理解
2011/01/17 Javascript
js获取事件源及触发该事件的对象
2013/10/24 Javascript
jquery 判断滚动条到达了底部和顶端的方法
2014/04/02 Javascript
jQuery中:contains选择器用法实例
2014/12/30 Javascript
jQuery实现默认是闭合的FAQ展开效果菜单
2015/09/14 Javascript
使用Ajax生成的Excel文件并下载的实例
2016/11/21 Javascript
jquery获取下拉框中的循环值
2017/02/08 Javascript
使用json-server简单完成CRUD模拟后台数据的方法
2018/07/12 Javascript
VeeValidate 的使用场景以及配置详解
2019/01/11 Javascript
微信小程序实现图片上传
2019/05/23 Javascript
JQuery实现简单的复选框树形结构图示例【附源码下载】
2019/07/16 jQuery
vue+webpack 更换主题N种方案优劣分析
2019/10/28 Javascript
[01:09]DOTAPLUS——DOTA2的新时代
2018/04/04 DOTA
浅析python 中__name__ = '__main__' 的作用
2014/07/05 Python
Python functools模块学习总结
2015/05/09 Python
python利用MethodType绑定方法到类示例代码
2017/08/27 Python
python调用xlsxwriter创建xlsx的方法
2018/05/03 Python
Python函数中的可变长参数详解
2019/09/12 Python
Python的scikit-image模块实例讲解
2020/12/30 Python
欧洲有机婴儿食品最大的市场:Organic Baby Food(供美国和加拿大)
2018/03/28 全球购物
商务助理岗位职责
2013/11/13 职场文书
企事业单位求职者的自我评价
2013/12/28 职场文书
大学第二课堂活动总结
2014/07/08 职场文书
四风个人对照检查材料思想汇报
2014/09/25 职场文书
井冈山红色之旅感想
2014/10/07 职场文书
2014年单位法制宣传日活动总结
2014/11/01 职场文书
2014年残联工作总结
2014/11/21 职场文书
先进教育工作者事迹材料
2014/12/23 职场文书
硕士毕业论文导师评语
2014/12/31 职场文书
探讨Java中的深浅拷贝问题
2021/06/26 Java/Android
windows server 2016 域环境搭建的方法步骤(图文)
2022/06/25 Servers