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 相关文章推荐
javascript实现的动态添加表单元素input,button等(appendChild)
Nov 24 Javascript
javascript控制frame,iframe的src属性代码
Dec 31 Javascript
利用jquery.qrcode在页面上生成二维码且支持中文
Feb 12 Javascript
js获取 type=radio 值的方法
May 09 Javascript
jQuery中end()方法用法实例
Jan 08 Javascript
学习使用grunt来打包JavaScript和CSS程序的教程
Jan 04 Javascript
JS实现新建文件夹功能
Jun 17 Javascript
使用JavaScript根据图片获取条形码的方法
Jul 04 Javascript
vue工程全局设置ajax的等待动效的方法
Feb 22 Javascript
微信小程序和百度的语音识别接口详解
May 06 Javascript
vue中的v-if和v-show的区别详解
Sep 01 Javascript
jQuery 选择器用法基础入门示例
Jan 04 jQuery
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
反射调用private方法实践(php、java)
2015/12/21 PHP
PHP加密3DES报错 Call to undefined function: mcrypt_module_open() 如何解决
2016/04/17 PHP
PHP区块查询实现方法分析
2018/05/12 PHP
让你的博文自动带上缩址的实现代码,方便发到微博客上
2010/12/28 Javascript
推荐11款jQuery开发的复选框和单选框美化插件
2011/08/02 Javascript
深入理解JavaScript系列(3) 全面解析Module模式
2012/01/15 Javascript
高性能Javascript笔记 数据的存储与访问性能优化
2012/08/02 Javascript
解析使用JS 清空File控件的路径值
2013/07/08 Javascript
JavaScript 异常处理 详解
2015/02/06 Javascript
jQuery插件pagination实现分页特效
2015/04/12 Javascript
早该知道的7个JavaScript技巧
2016/06/21 Javascript
JavaScript 是什么意思
2016/09/22 Javascript
JS实现简单的天数计算器完整实例
2017/04/28 Javascript
node.js中grunt和gulp的区别详解
2017/07/17 Javascript
利用require.js与angular搭建spa应用的方法实例
2017/07/19 Javascript
通过js控制时间,一秒一秒自己动的实例
2017/10/25 Javascript
JavaScript伪数组用法实例分析
2017/12/22 Javascript
vue项目实现记住密码到cookie功能示例(附源码)
2018/01/31 Javascript
解决linux下node.js全局模块找不到的问题
2018/05/15 Javascript
JavaScript中变量提升与函数提升经典实例分析
2018/07/26 Javascript
浅析JS中什么是自定义react数据验证组件
2018/10/19 Javascript
JS hasOwnProperty()方法检测一个属性是否是对象的自有属性的方法
2021/01/29 Javascript
[01:28:43]2014 DOTA2华西杯精英邀请赛5 24 DK VS CIS
2014/05/25 DOTA
[56:57]LGD vs VP 2019DOTA2国际邀请赛淘汰赛 胜者组赛BO3 第一场 8.20.mp4
2019/08/22 DOTA
使用Python读写及压缩和解压缩文件的示例
2016/07/08 Python
pycharm配置git(图文教程)
2019/08/16 Python
Python使用random模块生成随机数操作实例详解
2019/09/17 Python
编译 pycaffe时报错:fatal error: numpy/arrayobject.h没有那个文件或目录
2020/11/29 Python
解决pycharm 格式报错tabs和space不一致问题
2021/02/26 Python
亚瑟士美国官网:ASICS美国
2017/02/01 全球购物
公司周年庆典邀请函
2014/01/12 职场文书
2014年班主任自我评价范文
2014/04/23 职场文书
庆国庆国旗下讲话稿2014
2014/09/21 职场文书
2014年大学学生会工作总结
2014/12/02 职场文书
撤诉申请书法院范本
2015/05/18 职场文书
2016年猴年新春致辞
2015/08/01 职场文书