React 模拟实现全局Toast提示

用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拉群

猜你喜欢

转载自blog.csdn.net/weixin_42333548/article/details/124662308