-
Notifications
You must be signed in to change notification settings - Fork 320
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)}>× 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;
}
``html
<script type='text/javascript' src="./dist/React.js"></script> <script type='text/javascript' src="./test/react.development.js"></script> <script type='text/javascript' src="./test/react-dom.development.js"></script><script type='text/javascript' src="./lib/babel.js"></script>
<pre>应该等于
</pre>
<h1 id='root' class="root">
</h1>
<script type='text/babel'>
var container = document.getElementById("example")
var div = container
var PropTypes = React.PropTypes
if(!window.ReactDOM){
window.ReactDOM = React
}
var expect = function(a) {
return {
toBe: function(b) {
console.log(a,"vs", b, a === b)
},
toEqual(b){
console.log(a,"vs", b, a +""=== b+"")
},
toThrow(b){
try{
a()
}catch(e){
console.log(e,"vs", b, e.message +""=== b+"")
}
}
}
}
const Hello = ({ name }) => <h1>Hello {name}!</h1>;
class ShowMyError extends React.Component {
constructor(props) {
super(props);
this.state = { error: false };
}
componentDidCatch(error, info) {
console.log(error, info)
this.setState({ error, info });
}
render() {
if (this.state.error) {
return (
<div>
<h1>
Error AGAIN: {this.state.error.toString()}
</h1>
{this.state.info &&
this.state.info.componentStack.split("\n").map(i => {
return (
<div key={i}>
{i}
</div>
);
})}
</div>
);
}
return this.props.children;
}
}
class Broken extends React.Component {
constructor(props) {
super(props);
this.state = { throw: false, count: 0 };
}
render() {
if (this.state.throw) {
throw new Error("YOLO");
}
return (
<div>
<button
onClick={e => {
this.setState({ throw: true });
}}
>
button will render error.
</button>
<button onClick={e => {
this.setState(({ count }) => ({
count: count + 1
}));
}}>button will not throw</button>
<div>
{"All good here. Count: "}{this.state.count}
</div>
</div>
);
}
}
class App extends React.Component {
render() {
const styles = {
fontFamily: "sans-serif",
textAlign: "center"
};
return (
<div style={styles}>
<Hello name="ShowMyError" />
<h2>
Start clicking to see some {"\u2728"}magic{"\u2728"}
</h2>
<ShowMyError>
<Broken />
</ShowMyError>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));