啊哈,最近周末偷了下懒,去看LPL半决赛了,虽然中国队伍没有进入决赛,可是仍是打心底支持中国队,但愿他们可以在将来取得更好的成绩。html
这个组件的index文件就有不少代码,590行代码,并且在头部引入的额外文件特别的多,因此咱们这一个组件就先从这些额外的组件中开始吧,先看看这些外部方法可以作些什么。node
// index.js 头部
import PropTypes from 'prop-types';
import { findDOMNode, createPortal } from 'react-dom';
import createReactClass from 'create-react-class';
import contains from 'rc-util/lib/Dom/contains';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import Popup from './Popup';
import { getAlignFromPlacement, getPopupClassNameFromAlign } from './utils';
import getContainerRenderMixin from 'rc-util/lib/getContainerRenderMixin';
import Portal from 'rc-util/lib/Portal';复制代码
在官网这里有这么一个解释react
ReactDOM.createPortal(child, container)复制代码
Creates a portal. Portals provide a way to render children into a DOM node that exists outside the hierarchy of the DOM component.
es6
这个函数是用来建立一个portal
,而这个Portal
是提供一个方法来在指定的dom元素渲染一些组件的方法。api
这个函数也是可以在官网这里上找到的,是用来建立一个raect类而不是用es6语法的方法,在里面可使用getDefaultProps()
方法app
建立当前组件的默认props,可使用getInitialState()
建立当前组件的初始state,而且在里面写的方法都会自动的绑定上this
,dom
也就是他所说的Autobinding
,还有一个最有用的属性Mixins
,这个是可以在编写不少的可以使用的外部方法传入组件的属性。ide
这两个函数都是rc-util/lib/Dom/
里面的工具函数,接下来咱们分辨看看这两个函数可以作啥函数
// contains.js
// 这个函数是用来判断传入根节点root是否包含传入节点n,
// 若是包含则返回true,否者返回false
export default function contains(root, n) {
let node = n;
while (node) {
if (node === root) {
return true;
}
node = node.parentNode;
}
return false;
}复制代码
// addEventListener.js
// 这个函数主要的聚焦点是ReactDOM.unstable_batchedUpdates
// 这个api是没有公开的一个api,可是可使用,为了是想要将当前的组件状态强制性的
// 更新到组件内部去而且,可是这样作的目的可能有点粗暴。。
// 想要了解的能够看这篇文章,或许你有新的想法
// https://zhuanlan.zhihu.com/p/20328570
import addDOMEventListener from 'add-dom-event-listener';
import ReactDOM from 'react-dom';
export default function addEventListenerWrap(target, eventType, cb) {
/* eslint camelcase: 2 */
const callback = ReactDOM.unstable_batchedUpdates ? function run(e) {
ReactDOM.unstable_batchedUpdates(cb, e);
} : cb;
return addDOMEventListener(target, eventType, callback);
}复制代码
接下来是这两个函数,都是来自于rc-util/lib/
工具
// getContainerRenderMixin.js
import ReactDOM from 'react-dom';
function defaultGetContainer() {
const container = document.createElement('div');
document.body.appendChild(container);
return container;
}
export default function getContainerRenderMixin(config) {
const {
autoMount = true,
autoDestroy = true,
isVisible,
getComponent,
getContainer = defaultGetContainer,
} = config;
let mixin;
function renderComponent(instance, componentArg, ready) {
if (!isVisible || instance._component || isVisible(instance)) {
// 若是有isVisible,而且传入的实例有_component,而且isVisible返回真则进行一下代码
if (!instance._container) {
// 若是传入实例没有_container,则为其添加一个默认的
instance._container = getContainer(instance);
}
let component;
if (instance.getComponent) {
// 若是传入实例有getComponent,则将传入的参数传入实例的getComponent函数
component = instance.getComponent(componentArg);
} else {
// 不然就进行就是用传入参数中的getComponent方法构造一个Component
component = getComponent(instance, componentArg);
}
// unstable_renderSubtreeIntoContainer是更新组件到传入的DOM节点上
// 可使用它完成在组件内部实现跨组件的DOM操做
// ReactComponent unstable_renderSubtreeIntoContainer(
// parentComponent component,
// ReactElement element,
// DOMElement container,
// [function callback]
// )
ReactDOM.unstable_renderSubtreeIntoContainer(instance,
component, instance._container,
function callback() {
instance._component = this;
if (ready) {
ready.call(this);
}
});
}
}
if (autoMount) {
mixin = {
...mixin,
// 若是是自动渲染组件,那就在DidMount和DidUpdate渲染组件
componentDidMount() {
renderComponent(this);
},
componentDidUpdate() {
renderComponent(this);
},
};
}
if (!autoMount || !autoDestroy) {
mixin = {
// 若是不是自动渲染的,那就在mixin中添加一个渲染函数
...mixin,
renderComponent(componentArg, ready) {
renderComponent(this, componentArg, ready);
},
};
}
function removeContainer(instance) {
// 用于在挂载节点remove掉添加的组件
if (instance._container) {
const container = instance._container;
// 先将组件unmount
ReactDOM.unmountComponentAtNode(container);
// 而后在删除挂载点
container.parentNode.removeChild(container);
instance._container = null;
}
}
if (autoDestroy) {
// 若是是自动销毁的,那就在WillUnmount的时候销毁
mixin = {
...mixin,
componentWillUnmount() {
removeContainer(this);
},
};
} else {
mixin = {
// 若是不是自动销毁,那就只是在mixin中添加一个销毁的函数
...mixin,
removeContainer() {
removeContainer(this);
},
};
}
// 最后返回构建好的mixin
return mixin;
}复制代码
// Portal.js
// 这个函数就像咱们刚才上面所提到的Potal组件的一个编写,这样的组件很是有用
// 咱们能够利用这个组件建立在一些咱们所须要建立组件的地方,好比在body节点建立
// 模态框,或者在窗口节点建立fixed的定位的弹出框之类的。
// 还有就是在用完这个组件也就是在componentWillUnmount的时候必定要将节点移除
import React from 'react';
import PropTypes from 'prop-types';
import { createPortal } from 'react-dom';
export default class Portal extends React.Component {
static propTypes = {
getContainer: PropTypes.func.isRequired,
children: PropTypes.node.isRequired,
}
componentDidMount() {
this.createContainer();
}
componentWillUnmount() {
this.removeContainer();
}
createContainer() {
this._container = this.props.getContainer();
this.forceUpdate();
}
removeContainer() {
if (this._container) {
this._container.parentNode.removeChild(this._container);
}
}
render() {
if (this._container) {
return createPortal(this.props.children, this._container);
}
return null;
}
}复制代码