Skip to content

ReactDOM.createPortal

司徒正美 edited this page Nov 14, 2017 · 4 revisions

createPortal是依赖新设计的diffChildren方法,能处理多个节点的移除与添加。 弹窗里面的事件能冒泡到生成的弹窗的容器上来,因此我也重构了事件系统,让它基于虚拟DOM树冒泡。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
   <script src="./dist/React.js"></script>
   <!--<script src="./test/react.development.js"></script>
    <script src="./test/react-dom.development.js"></script>-->

    <script src="./test/babel.js"></script>
   

</head>
<body>

    <pre>
            
    </pre>
    <div id='example'></div>
    <div id='console'></div>
    <script type='text/babel'>
        var expect = function(a) {
            return {
                toBe: function(b) {
                    console.log(a, b, a === b)
                }
            }
        }
        var PropTypes = React.PropTypes

        var container = document.getElementById("example")
      
        class Container extends React.Component {
          constructor(props) {
            super(props);
            this.state = {
              show: false,
              xxx: 1
            }
          }
          
          _show() {
            if(this.state.show){
                return //防止创建多个弹窗
            }
            console.log("show")
            document.getElementById('console').innerHTML += 'SHOW<br>';
            this.setState({show: true});
          }
          
          _close(e) {
           // e.stopPropagation()
            console.log(e.target)
            console.log("hide")
            document.getElementById('console').innerHTML = '';
            this.setState({show: false});
          }
          
          render() {
            const {show} = this.state;
            
            return (
                <div className="Container" onClick={()=>{ console.log("能接受到Portal冒泡上来的事件")}}>
              <div className="hasClick" style={{background: '#00bcd4'}} onClick={this._show.bind(this)}>
                <div>Click me to show the Portal content</div>
                <div>State: {show && 'visible' || 'hidden'}</div>
                {show && <Portal>
                  <div style={{background: '#ffeebb',height:200}}>{this.state.xxx}
                    <button onClick={this._close.bind(this)}>&times; close portal</button>
                  </div>
                </Portal>}
              </div>
              </div>
            )
          }
        }
        
        class Portal extends React.Component{
          constructor(props) {
            super(props);
            this.node = document.createElement('div');
            this.node.className = "dynamic"
            document.body.appendChild(this.node);
          }
          componentWillUnmount(){
           // document.body.removeChild(this.node);
          }
          
          render() {
            return ReactDOM.createPortal(this.props.children, this.node);
          }
        }
       var s = ReactDOM.render(<Container />, document.getElementById('example'));
        
        
               </script>
</body>

</html>

事件系统的重要改变

function collectPaths(from, end) {
    var paths = [];
    var node = from;
    while (node && !node.__events) {
        node = node.parentNode;
        if (end === from) {
            return paths;
        }
    }
    if(!node || node.nodeType >1 ){//如果跑到document上
        return paths;
    }
    var vnode = node.__events.vnode;
    do {
        if (vnode.vtype === 1) {
            var dom = vnode.stateNode;
            if (dom === end) {
                break;
            }
            if (dom.__events) {
                paths.push({ dom: dom, events: dom.__events });
            }
        }
    } while ((vnode = vnode.return));// eslint-disable-line
    return paths;
}