Portals 传送门

React Portal之所以叫Portal,因为做的就是和“传送门”一样的事情:render到一个组件里面去,实际改变的是网页上另一处的DOM结构。
实际上解决的痛点:一个组件的交互涉及到一个非父类组件 这时候要么用全局状态管理传数据 要么通过"传送门"直接推一个子组件过去

传送门 v16之前的实现:
基于API:
unstable_renderSubtreeIntoContainer
unmountComponentAtNode

import React from 'react';
import {unstable_renderSubtreeIntoContainer, unmountComponentAtNode}
from 'react-dom';

class Dialog extends React.Component {
render() {
return null;
}

componentDidMount() {
const doc = window.document;
this.node = doc.createElement('div');
doc.body.appendChild(this.node);

this.renderPortal(this.props);

}

componentDidUpdate() {
this.renderPortal(this.props);
}

componentWillUnmount() {
unmountComponentAtNode(this.node);
window.document.body.removeChild(this.node);
}

renderPortal(props) {
unstable_renderSubtreeIntoContainer(
this, //代表当前组件
<div class="dialog">
{props.children}
</div>, // 塞进传送门的JSX
this.node // 传送门另一端的DOM node
);
}
}

总结:
它什么都不给自己画,render返回一个null就够了;
它做得事情是通过调用renderPortal把要画的东西画在DOM树上另一个角落。

传送门 v16的实现:
在v16中,使用Portal创建Dialog组件简单多了,不需要牵扯到componentDidMount、componentDidUpdate,也不用调用API清理Portal,关键代码在render中,像下面这样就行。(脏活render()都干了)

import React from 'react';
import {createPortal} from 'react-dom';

class Dialog extends React.Component {
constructor() {
super(...arguments);
const doc = window.document;
this.node = doc.createElement('div');
doc.body.appendChild(this.node);
}

render() {
return createPortal(
<div class="dialog">
{this.props.children}
</div>, //塞进传送门的JSX
this.node //传送门的另一端DOM node
);
}

componentWillUnmount() {
window.document.body.removeChild(this.node);
}
}

3.1官方的v16传送门实现
// These two containers are siblings in the DOM
const appRoot = document.getElementById('app-root');
const modalRoot = document.getElementById('modal-root');

// Let's create a Modal component that is an abstraction around
// the portal API.
class Modal extends React.Component {
constructor(props) {
super(props);
// Create a div that we'll render the modal into. Because each
// Modal component has its own element, we can render multiple
// modal components into the modal container.
this.el = document.createElement('div');
}

componentDidMount() {
// Append the element into the DOM on mount. We'll render
// into the modal container element (see the HTML tab).
modalRoot.appendChild(this.el);
}

componentWillUnmount() {
// Remove the element from the DOM when we unmount
modalRoot.removeChild(this.el);
}

render() {
// Use a portal to render the children into the element
return ReactDOM.createPortal(
// Any valid React child: JSX, strings, arrays, etc.
this.props.children,
// A DOM element
this.el,
);
}
}

  1. v16之前传送出去的组件不冒泡回来了(应该是在传送出去的那一端冒泡 有待考证) v16之后传送出去的组件事件会冒泡回来
    // These two containers are siblings in the DOM
    const appRoot = document.getElementById('app-root');
    const modalRoot = document.getElementById('modal-root');

class Modal extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement('div');
}

componentDidMount() {
modalRoot.appendChild(this.el);
}

componentWillUnmount() {
modalRoot.removeChild(this.el);
}

render() {
return ReactDOM.createPortal(
this.props.children,
this.el,
);
}
}

class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {clicks: 0};
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
// This will fire when the button in Child is clicked,
// updating Parent's state, even though button
// is not direct descendant in the DOM.
this.setState(prevState => ({
clicks: prevState.clicks + 1
}));
}

render() {
return (
<div onClick={this.handleClick}>
<p>Number of clicks: {this.state.clicks}</p>
<p>
Open up the browser DevTools
to observe that the button
is not a child of the div
with the onClick handler.
</p>
<Modal>
<Child />
</Modal>
</div>
);
}
}

function Child() {
// The click event on this button will bubble up to parent,
// because there is no 'onClick' attribute defined
return (
<div className="modal">
<button>Click</button>
</div>
);
}

ReactDOM.render(<Parent />, appRoot);

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 3. JSX JSX是对JavaScript语言的一个扩展语法, 用于生产React“元素”,建议在描述UI的时候...
    pixels阅读 2,925评论 0 24
  • This project was bootstrapped with Create React App. Belo...
    unspecx阅读 5,254评论 0 2
  • 你是否每天穿梭在钢筋水泥的丛林中,过着朝九晚五的生活;你是否每天进出高耸入云的写字楼,却...
    寂川Y阅读 152评论 0 0
  • ECMAScript6 ECMAScript 6 也称为ECMAScript2015,是2015年出版的ECMAS...
    一刀一个小黄鱼阅读 674评论 0 49
  • 园里桃花孤零零的铺了满地,只余那梢头最后一朵。她悲戚地,痛苦地,迷茫地走过去,伸手想触摸那摇摇欲坠,却是在指尖还未...
    崔九阅读 733评论 0 4