ReactDOM.createPortal(ReactNod,container)
实现弹窗的关键在于,如何将弹窗组件挂载到任意的dom上.这里react-dom中提供了一个方法,下面举个例子
import React from 'react';
import ReactDOM from 'react-dom';
const Modal = (props) => {
const { container } = props;
return ReactDOM.createPortal(
// 这里可以是dom元素,也可以是react组件,还可以是react节点
<div>我是弹窗的容器</div>,
// 将传入的元素/组件/节点,挂载到container下面
container
)
}
这样一个简单的例子,就已经完成了一个组件的挂载,有的时候,我们需要在函数中,或是一个事件中使用一个消息弹窗.但是我们又不想使用一个消息组件.最好是有静态方法,调用一次,弹窗就弹一次,那么这个功能如何实现呢?
ReactDOM.render(ReactNode,container)
import React, { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
import styles from './index.module.less'
const Modal = (props) => {
const { container } = props;
return ReactDOM.createPortal(
// 这里可以是dom元素,也可以是react组件,还可以是react节点
<div style={{ position: 'absolute', top: '50%', left: '50%', zIndex: 99999999, width: '500px', height: '500px', backgroundColor: 'red' }}>我是弹窗的容器</div>,
// 将传入的元素/组件/节点,挂载到container下面
container
)
}
const Message = (props) => {
const { visible, onCancel, container, onFinish, msg, ...rest } = props;
const [_container, setContainer] = useState(null);
const timer = useRef<any>(null);
useEffect(
() => {
if (visible) return;
if (typeof onFinish === 'function') {
onFinish();
}
},
[visible]
)
useEffect(
() => {
setContainer(container || document.body)
},
[container]
);
useEffect(
() => {
const cb = (e) => {
const ele = e.target;
if (ele && ele.className !== styles.overlay) {
onClose();
onClear();
}
}
document.addEventListener('click', cb);
return () => {
document.removeEventListener('click', cb);
}
},
[]
);
useEffect(
() => {
if (!!visible) {
if (!!timer.current) clearTimeout(timer.current);
timer.current = setTimeout(() => {
onClose();
onClear();
}, 3000)
} else {
onClear();
}
},
[visible, onCancel, onFinish]
)
function onClose() {
if (typeof onCancel === 'function') {
onCancel();
}
}
function onClear() {
clearTimeout(timer.current);
timer.current = null;
}
console.log(_container)
if (!_container) return null;
return (
<Modal
className={styles.overlay}
visible={visible}
onClick={(e) => { e.stopImmediatePropagation() }}
container={_container}
{...rest}
>
<div>{msg}</div>
</Modal>
)
};
let msgBox: any = null;
// type弹窗类型,可以根据不同类型,渲染不同类型弹窗的样式
const MessageAPI = (type, msg, callBack) => {
onCancel();
msgBox = document.createElement('div');
document.body.appendChild(msgBox);
function onCancel() {
if (!!msgBox) {
ReactDOM.unmountComponentAtNode(msgBox);
}
}
console.log(msgBox)
ReactDOM.render(
<Message
visible={true}
onCancel={onCancel}
onFinish={() => !!callBack && callBack()}
msg={msg}
/>,
msgBox
);
}
// 这样就可以通过Message.onSuccess("成功了")这样的静态方法,使用弹窗了
Message.onSuccess = (msg, callback?: any) => MessageAPI('success', msg, callback);
Message.onError = (msg, callback?: any) => MessageAPI('error', msg, callback);
export default Message;
效果图