mask(遮罩)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 组件

最简单版本的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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

https://stackoverflow.com/questions/54672784/animating-react-transition-group-with-reactdom-createportal

有人说给他加 animationDuration

https://codesandbox.io/s/stupefied-bouman-ehszt?file=/src/components/Portal/index.js:517-526

一定会有 div

节点从有到无

image-20211215181234263

animatedVisible 成为 true,显示Portal 组件,先渲染父组件,再渲染子组件

先执行

破除魔咒,取消

1
2
3
4
5
6
7
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();

1
2
3
4
5
6
7
8
9
10
11
<Button
size="xs"
onClick={() => {
loading.show();
setTimeout(() => {
loading.hide();
}, 3000);
}}
>
开启
</Button>

这里有个思考角度,loading 需不需要被 popup 包裹,我的想法是按照实际需求来做组件,我们可以做成像弹出层那样,但是 loading 的用法,一般是作为 Toast 的子组件来使用,所以这里,useLoading 对我们不适用

当然,做这个是为了铺垫 useToast,它也需要具备 Toast.show() 的用法

show 的时候 ReactDOM.render 过程