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 相关文章推荐
js鼠标点击图片实现随机变换图片的方法
Feb 16 Javascript
Javascript之Math对象详解
Jun 07 Javascript
js中遍历Map对象的方法
Jul 27 Javascript
JavaScript无操作后屏保功能的实现方法
Jul 04 Javascript
Bootstrap table使用方法汇总
Nov 17 Javascript
vue 动态修改a标签的样式的方法
Jan 18 Javascript
Postman模拟发送带token的请求方法
Mar 31 Javascript
微信小程序实现卡片左右滑动效果的示例代码
May 01 Javascript
js中arguments对象的深入理解
May 14 Javascript
从零撸一个pc端vue的ui组件库( 计数器组件 )
Aug 08 Javascript
js时间转换毫秒的实例代码
Aug 21 Javascript
vue项目中使用eslint+prettier规范与检查代码的方法
Jan 16 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乱码问题,使用SET NAMES utf8校正
2009/11/30 PHP
php给一组指定关键词添加span标签的方法
2015/03/31 PHP
Yii视图CGridView实现操作按钮定义地址示例
2016/07/14 PHP
PHP flush 函数使用注意事项
2016/08/26 PHP
JavaScript 常见对象类创建代码与优缺点分析
2009/12/07 Javascript
iframe子父页面调用js函数示例
2013/11/07 Javascript
js 鼠标移动显示图片的简单实例
2013/12/25 Javascript
JS取request值以及自动执行使用示例
2014/02/24 Javascript
jquery中checkbox全选失效的解决方法
2014/12/26 Javascript
JS自动倒计时30秒后按钮才可用(两种场景)
2015/08/31 Javascript
js实现input密码框提示信息的方法(附html5实现方法)
2016/01/14 Javascript
JavaScript地理位置信息API
2016/06/11 Javascript
JS异步文件分片断点上传的实现思路
2016/12/25 Javascript
jQuery简单获取DIV和A标签元素位置的方法
2017/02/07 Javascript
jquery.tableSort.js表格排序插件使用方法详解
2020/08/12 Javascript
Bootstrap 网格系统布局详解
2017/03/19 Javascript
基于zepto.js实现手机相册功能
2017/07/11 Javascript
详解vue-cli官方脚手架配置
2018/07/20 Javascript
详解ESLint在Vue中的使用小结
2018/10/15 Javascript
[56:35]DOTA2上海特级锦标赛主赛事日 - 5 总决赛Liquid VS Secret第一局
2016/03/06 DOTA
Python实现基于HTTP文件传输实例
2014/11/08 Python
Python中内置的日志模块logging用法详解
2016/07/12 Python
Python pass详细介绍及实例代码
2016/11/24 Python
机器学习python实战之手写数字识别
2017/11/01 Python
Python图像处理之识别图像中的文字(实例讲解)
2018/05/10 Python
python实现模拟器爬取抖音评论数据的示例代码
2021/01/06 Python
您的健身减肥和健康饮食专家:vitafy
2017/06/06 全球购物
Space NK英国站:英国热门美妆网站
2017/12/11 全球购物
在购买印度民族服饰:Soch
2020/09/15 全球购物
会计电算化学生个人的自我评价
2014/02/08 职场文书
土木工程师职业规划范文
2014/03/07 职场文书
研究生考核个人自我鉴定
2014/03/27 职场文书
Python打包为exe详细教程
2021/05/18 Python
MySQL通过binlog恢复数据
2021/05/27 MySQL
PYTHON InceptionV3模型的复现详解
2022/05/06 Python
keepalived + nginx 实现高可用方案
2022/12/24 Servers