搞轮子:不依赖外部DIY Toast 需要考虑什么
mask(遮罩)
import React, { FC, memo } from "react";
import classnames from "classnames";
import { MaskProps } from "./PropType";
const prefixCls = "jing-mask";
const Mask: FC<MaskProps> = (props) => {
  const { className, type, visible, style, onClick } = props;
  const classes = classnames(className, prefixCls, {
    [`${prefixCls}--${type}`]: !!type,
  });
  return visible ? (
    <div className={classes} style={style} onClick={onClick} />
  ) : null;
};
Mask.defaultProps = {
  type: "normal",
  visible: false,
};
export default memo(Mask);
缺点在于它
前提
animation(transition) 组件
mask(遮罩)组件(搞定)
portal 组件
最简单版本的
import React, { FC, useState, useEffect, memo } from "react";
import { createPortal } from "react-dom";
import { PortalProps } from "./PropType";
const Portal: FC<PortalProps> = (props) => {
  const { children, container, className } = props;
  const [containerEl] = useState(() => {
    const el = document.createElement("div");
    el.className += `jing-portal__container ${className}`;
    return el;
  });
  useEffect(() => {
    document.body.appendChild(container || containerEl);
    return () => {
      document.body.removeChild(containerEl);
    };
  }, []);
  return createPortal(children, containerEl);
};
export default memo(Portal);
定位 popup,弹出层
popup 组件
使用方式
当作静态函数使用
Toast(…)
Toast.loading()
组件本身要考虑什么功能
遮罩(mask)、内容(message)、是否禁止背景点击(forbidClick),支持自定义图标(icon)、自定义出现的位置(position)、是否在点击遮罩层后关闭(closeOnClickOverlay)
展示时长(duration)、关闭时的回调函数(onClose)
提供 Toast.success 表示成功,
Toast.fail 表示失败
Toast.loading 表示加载
单例模式
allowMultiple
梳理一下
Toast的基础是 Popup,Popup的基础是 Mask 和 Portal
Popup要做的就是弹出层,属于基础组件
Modal 要做之前 popup.alert 之类的事情(Vant 是 Dialog)
Toast 要实例化
无论是 Toast 还是 Modal 都需要使用静态方法调用
Modal 可以大写
popup 和 portal 放一起
不可见的时候看不到元素,
可见的时候渲染元素
动画
react-transition-group 和 portal 的结合
因为 portal 是return 出的组件,所以不会有动画
需要做 animation
有人说给他加 animationDuration
https://codesandbox.io/s/stupefied-bouman-ehszt?file=/src/components/Portal/index.js:517-526
一定会有 div
节点从有到无

animatedVisible 成为 true,显示Portal 组件,先渲染父组件,再渲染子组件
先执行
破除魔咒,取消
useEffect(() => {
  mountContainer?.appendChild(containerEl);
  return () => {
    mountContainer?.removeChild(containerEl);
  };
}, []);
react-transition-group 的用法
http://reactcommunity.org/react-transition-group/transition#Transition-prop-onExited
Toast 不是一个组件,而是一个 ”方法“, Toast("提示内容")
怎么把一个 <Toast>提示文字<Toast> 写法的组件改造成 Toast("提示内容")
查看了别人做的 Toast,
先做个 React 版本的 Toast,即组件时写法,然后再将它作为基础组件使用,怎么使用,
生成一个 div(document.createElement(‘div’))
插入 dom 中(bodyContainer.appendChild(container))
ReactDOM.render() 渲染它
我们先完成 Toast 组件
Toast.loading()
需要完成 loading 组件
loading 之后
要 useloading
show
hide
目的是把它当作一个静态函数使用
Loading.
const loading = Loading.useLoading();
<Button
  size="xs"
  onClick={() => {
    loading.show();
    setTimeout(() => {
      loading.hide();
    }, 3000);
  }}
>
  开启
</Button>
这里有个思考角度,loading 需不需要被 popup 包裹,我的想法是按照实际需求来做组件,我们可以做成像弹出层那样,但是 loading 的用法,一般是作为 Toast 的子组件来使用,所以这里,useLoading 对我们不适用
当然,做这个是为了铺垫 useToast,它也需要具备 Toast.show() 的用法
show 的时候 ReactDOM.render 过程