[react] react-modal 源码
看看思路
//Modal
function getParentElement(parentSelector) {
return parentSelector();
}
componentDidMount() {
if (!canUseDOM) return;//判断是否能取到真实dom
const isReact16 = ReactDOM.createPortal !== undefined;
if (!isReact16) {
this.node = document.createElement("div");
}
this.node.className = this.props.portalClassName;
//parentSelector为产生父容器的方法
const parent = getParentElement(this.props.parentSelector);
parent.appendChild(this.node);
!isReact16 && this.renderPortal(this.props);
}
componentWillReceiveProps(newProps) {
if (!canUseDOM) return;
const { isOpen } = newProps;
// Stop unnecessary renders if modal is remaining closed
if (!this.props.isOpen && !isOpen) return;
const currentParent = getParentElement(this.props.parentSelector);
const newParent = getParentElement(newProps.parentSelector);
if (newParent !== currentParent) {
currentParent.removeChild(this.node);
newParent.appendChild(this.node);
}
!isReact16 && this.renderPortal(newProps);
}
componentWillUpdate(newProps) {
if (!canUseDOM) return;
if (newProps.portalClassName !== this.props.portalClassName) {
this.node.className = newProps.portalClassName;
}
}
componentWillUnmount() {
if (!canUseDOM || !this.node || !this.portal) return;
const state = this.portal.state;
const now = Date.now();
const closesAt =
state.isOpen &&
this.props.closeTimeoutMS &&
(state.closesAt || now + this.props.closeTimeoutMS);
if (closesAt) {
if (!state.beforeClose) {
this.portal.closeWithTimeout();
}
setTimeout(this.removePortal, closesAt - now);
} else {
this.removePortal();
}
}
removePortal = () => {
!isReact16 && ReactDOM.unmountComponentAtNode(this.node);
const parent = getParentElement(this.props.parentSelector);
parent.removeChild(this.node);
};
portalRef = ref => {
this.portal = ref;
};
renderPortal = props => {
const portal = createPortal(
this,
<ModalPortal defaultStyles={Modal.defaultStyles} {...props} />,
this.node
);
this.portalRef(portal);
};
render() {
if (!canUseDOM || !isReact16) {
return null;
}
if (!this.node && isReact16) {
this.node = document.createElement("div");
}
return createPortal(
<ModalPortal
ref={this.portalRef}
defaultStyles={Modal.defaultStyles}
{...this.props}
/>,
this.node
);
}
//ModalPortal
render() {
const { className, overlayClassName, defaultStyles } = this.props;
const contentStyles = className ? {} : defaultStyles.content;
const overlayStyles = overlayClassName ? {} : defaultStyles.overlay;
return this.shouldBeClosed() ? null : (
<div
ref={this.setOverlayRef}
className={this.buildClassName("overlay", overlayClassName)}
style={{ ...overlayStyles, ...this.props.style.overlay }}
onClick={this.handleOverlayOnClick}
onMouseDown={this.handleOverlayOnMouseDown}
onMouseUp={this.handleOverlayOnMouseUp}
>
<div
ref={this.setContentRef}
style={{ ...contentStyles, ...this.props.style.content }}
className={this.buildClassName("content", className)}
tabIndex="-1"
onKeyDown={this.handleKeyDown}
onMouseDown={this.handleContentOnMouseDown}
onMouseUp={this.handleContentOnMouseUp}
onClick={this.handleContentOnClick}
role={this.props.role}
aria-label={this.props.contentLabel}
{...this.ariaAttributes(this.props.aria || {})}
>
{this.props.children}
</div>
</div>
);
}
//example1
function getParent() {
return document.querySelector('#root');
}
<Modal
...
parentSelector={getParent}
...
>
<p>Modal Content.</p>
</Modal>
<Modal
...
className={{
base: 'myClass',
afterOpen: 'myClass_after-open',
beforeClose: 'myClass_before-close'
}}
overlayClassName={{
base: 'myOverlayClass',
afterOpen: 'myOverlayClass_after-open',
beforeClose: 'myOverlayClass_before-close'
}}
...
>
//example2
import React from 'react';
import ReactDOM from 'react-dom';
import Modal from 'react-modal';
const customStyles = {
content : {
top : '50%',
left : '50%',
right : 'auto',
bottom : 'auto',
marginRight : '-50%',
transform : 'translate(-50%, -50%)'
}
};
class App extends React.Component {
constructor() {
super();
this.state = {
modalIsOpen: false
};
this.openModal = this.openModal.bind(this);
this.afterOpenModal = this.afterOpenModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
openModal() {
this.setState({modalIsOpen: true});
}
afterOpenModal() {
// references are now sync'd and can be accessed.
this.subtitle.style.color = '#f00';
}
closeModal() {
this.setState({modalIsOpen: false});
}
render() {
return (
<div>
<button onClick={this.openModal}>Open Modal</button>
<Modal
isOpen={this.state.modalIsOpen}
onAfterOpen={this.afterOpenModal}
onRequestClose={this.closeModal}
style={customStyles}
contentLabel="Example Modal"
>
<h2 ref={subtitle => this.subtitle = subtitle}>Hello</h2>
<button onClick={this.closeModal}>close</button>
<div>I am a modal</div>
<form>
<input />
<button>tab navigation</button>
<button>stays</button>
<button>inside</button>
<button>the modal</button>
</form>
</Modal>
</div>
);
}
}
ReactDOM.render(<App />, appElement);