用react模拟一个toast提示框,提示的位置可挂载到指定的dom,不指定dom的话默认会挂载到body上,具体逻辑自行调整即可
效果如下:
实现逻辑,index.tsx
import React, { useState, useEffect, useRef, forwardRef, useImperativeHandle, Ref } from 'react';
import styles from './index.less';
import ReactDOM from 'react-dom';
interface ToastProps {
widthStyle?: any;
contentText?: string | null;
containerEl?: any;
}
export interface ToastType {
show: (text: string) => void;
}
export const Toast = forwardRef(
(props: ToastProps, ref: Ref<ToastType>): React.ReactElement => {
const ToastRef: React.RefObject<HTMLDivElement> = useRef(null);
const [showToast, setShowToast] = useState<boolean>(false);
const [fadeIn, setFadeIn] = useState<boolean>(false);
const [contentText, setContentText] = useState<string>('');
useImperativeHandle(ref, () => ({
show: (text: string) => show(text),
}));
useEffect(() => {
if (!showToast) return;
const id = setTimeout(() => {
setFadeIn(false);
}, 2500);
return () => clearTimeout(id);
}, [showToast]);
useEffect(() => {
if (fadeIn || !showToast) return;
const id = setTimeout(() => {
setShowToast(false);
}, 500);
return () => clearTimeout(id);
}, [fadeIn, showToast]);
useEffect(() => {
if (!('contentText' in props)) return;
if (props.contentText) {
setContentText(props.contentText || '');
setFadeIn(true);
setShowToast(true);
} else if ('contentText' in props && props.contentText === null) {
setFadeIn(false);
setShowToast(false);
}
}, [props]);
const show = (text: string) => {
if (showToast) return;
setContentText(text);
setFadeIn(true);
setShowToast(true);
};
return ReactDOM.createPortal(
<div
ref={ToastRef}
className={`${styles.toast} ${fadeIn ? `${styles.fadeIn}` : `${styles.fadeOut}`}`}
style={
{ display: showToast ? 'block' : 'none', ...props.widthStyle }}
>
<div className={styles.toastInner}>
{/* <i className={styles.icon}></i> */}
<div className={styles.content}>
<span>{contentText}</span>
</div>
</div>
</div>,
props.containerEl || document.getElementById('wbContentContainer') || document.body,
);
},
);
index.less
.toast {
position: absolute;
top: 50%;
left: 50%;
z-index: 999;
display: none;
transform: translate(-50%, -50%);
&.fadeIn {
animation: keyframes-fadeIn 0.5s forwards;
}
&.fadeOut {
animation: keyframes-fadeOut 0.5s forwards;
}
.toastInner {
position: relative;
display: flex;
align-items: center;
width: auto;
max-width: 450px;
min-height: 40px;
padding: 0 18px 0 20px;
background: rgba(0, 0, 0, 0.8);
border-radius: 8px;
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.25);
.icon {
position: absolute;
top: 12px;
bottom: 0;
left: 18px;
width: 16px;
height: 16px;
margin-right: 4px;
}
.content {
padding: 8px 0;
color: #fff;
span {
display: -webkit-box;
color: #dadfe4;
font-weight: 400;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
}
}
}
}
@keyframes keyframes-fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes keyframes-fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
交流
1、QQ群:可添加qq群共同进阶学习: 进军全栈工程师疑难解 群号: 856402057
2、公众号:公众号「进军全栈攻城狮」 ,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!通过公众号可加我vx拉群