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 相关文章推荐
High Performance JavaScript(高性能JavaScript)读书笔记分析
May 05 Javascript
文本有关的样式和jQuery求对象的高宽问题分别说明
Aug 30 Javascript
jQuery实现自定义checkbox和radio样式
Jul 13 Javascript
JavaScript中匿名函数的用法及优缺点详解
Jun 01 Javascript
javascript 将共享属性迁移到原型中去的实现方法
Aug 31 Javascript
Bootstrap实现各种进度条样式详解
Apr 13 Javascript
微信小程序 开发之全局配置
May 05 Javascript
javascript checkbox/radio onchange不能兼容ie8处理办法
Jun 13 Javascript
详谈JS中数组的迭代方法和归并方法
Aug 11 Javascript
Angular6笔记之封装http的示例代码
Jul 27 Javascript
详解基于vue-cli3快速发布一个fullpage组件
Mar 08 Javascript
关于小程序优化的一些建议(小结)
Dec 10 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中的字符编码转换函数用法示例
2014/10/20 PHP
PHP编写简单的App接口
2016/08/28 PHP
jQuery 注意事项 与原因分析
2009/04/24 Javascript
TimergliderJS 一个基于jQuery的时间轴插件
2011/12/07 Javascript
jQuery 如何先创建、再修改、后添加DOM元素
2014/05/20 Javascript
Node.js抓取中文网页乱码问题和解决方法
2015/02/10 Javascript
体验jQuery和AngularJS的不同点及AngularJS的迷人之处
2016/02/02 Javascript
js多个物体运动功能实例分析
2016/12/20 Javascript
js 数据存储和DOM编程
2017/02/09 Javascript
js中toString()和String()区别详解
2017/03/23 Javascript
vue.js与后台数据交互的实例讲解
2018/08/08 Javascript
ES6基础之解构赋值(destructuring assignment)
2019/02/21 Javascript
vue 开发企业微信整合案例分析
2019/12/02 Javascript
简单了解vue 插值表达式Mustache
2020/07/22 Javascript
Vue 组件的挂载与父子组件的传值实例
2020/09/02 Javascript
[58:29]DOTA2-DPC中国联赛 正赛 Phoenix vs XG BO3 第一场 1月31日
2021/03/11 DOTA
Python 基础教程之str和repr的详解
2017/08/20 Python
详解TensorFlow在windows上安装与简单示例
2018/03/05 Python
在Pandas中DataFrame数据合并,连接(concat,merge,join)的实例
2019/01/29 Python
python实现简单日期工具类
2019/04/24 Python
python循环嵌套的多种使用方法解析
2019/11/29 Python
解决python-docx打包之后找不到default.docx的问题
2020/02/13 Python
基于Python中Remove函数的用法讨论
2020/12/11 Python
阻止移动设备(手机、pad)浏览器双击放大网页的方法
2014/06/03 HTML / CSS
Html5百叶窗效果的示例代码
2017/12/11 HTML / CSS
New Balance澳大利亚官网:运动鞋和健身服装
2019/02/23 全球购物
Molton Brown美国官网:奢华美容、香水、沐浴和身体护理
2020/09/02 全球购物
毕业生的自我评价分享
2013/12/18 职场文书
实用求职信范文分享
2013/12/25 职场文书
期末考试动员演讲稿
2014/01/10 职场文书
公司承诺书怎么写
2014/05/24 职场文书
2014年学生资助工作总结
2014/12/18 职场文书
导游词开场白
2015/01/31 职场文书
人事主管岗位职责
2015/02/04 职场文书
Python音乐爬虫完美绕过反爬
2021/08/30 Python
2022微信温控新功能上线
2022/05/09 数码科技