美文网首页
Portals 传送门

Portals 传送门

作者: 豆豆猫1031 | 来源:发表于2018-02-23 11:52 被阅读0次

    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);

    相关文章

      网友评论

          本文标题:Portals 传送门

          本文链接:https://www.haomeiwen.com/subject/vwztxftx.html