前言
本次阅读源码分析十分艰难,但也学习到不少知识,可能有不少错误希望指正。
请注意,本文代码经过大量删减,源码请看相关连接
控制消息最大条数
试想一下,在页面的上端不停的出现提示,但是最多只能显示5条,我们当然可以简单的不停的生成,然后控制他们的生命周期,但是我们无法确定到底有几条数据。
类似游戏中的对象池的概念,但也可以通过一个变量来存储所有消息对象就行了。
// Message.tsx
interface BaseNoticeState {
notices: { [key: string]: any }[];
position?: 'top' | 'bottom';
}
let messageInstance: Record<'top' | 'bottom',BaseNoticeState> | {} = {};
- 也就是说,我们顶部或者底部各有两个BaseNoticeState,管理各自的state
- messageInstance这个变量在全局是唯一的,即使Message被各个其他组件引用
消息组件
正向继承 & HOC & HOOK
- 正向继承就是父组件给子组件提供了一些公共方法,然后子组件也就用了这些功能,这里父组件似乎一般不具备渲染功能
class SignUpDialog extends SignUpDialogFather{}
- HOC也就是高阶组件,你可以将组件塞到高阶组件里面去,然后给这个组件加上额外的功能,再渲染出这个子组件
const EnhancedComponent = higherOrderComponent(WrappedComponent);
- Hook是完全将逻辑和视图完全剥离开来,页面继承也直接嵌套就行,逻辑复用直接用hook代替
const [count, setCount] = useState(0);
react-transition-group
这似乎是个提供动画的依赖,是react官方开发的
// Message.tsx
class Message extends BaseNotification {
// 这个先不管,最后会用到
static success: (config: MessageProps | string) => MessageType;
static info: (config: MessageProps | string) => MessageType;
...
render() {
// 这里拿到了state值,也就是因为BaseNotification 提供了
const { notices, position } = this.state;
return (
<div>
<TransitionGroup component={null}>
{notices.map((notice) => (
<CSSTransition
key={notice.id}
timeout={{
enter: 100,
exit: 300,
}}
>
// Notice提供具体的功能
<Notice
{...notice}
onClose={this.remove}
noticeType="message"
/>
</CSSTransition>
))}
</TransitionGroup>
</div>
);
}
}
// BaseNotification.tsx
// 继承的父组件
class BaseNotice extends Component<any, BaseNoticeState> {
constructor(props) {
super(props);
this.state = {
notices: [],
position: 'topRight',
};
this.remove = this.remove.bind(this);
}
add = (noticeProps) => {
};
}
添加实例方法
动态创建div以及渲染组件到div内部
- 通过原生方法createElement创建div
- 通过ReactDOM.createRoot(div as HTMLElement).render()渲染进去
Ref
下面这句话很重要,可能是字节大佬说的,说不定就是他写的arco
Props 是单向数据流,以 “声明式” 渲染组件;Ref 则是以 “命令式” 操作组件
那么我们这利用ref的回调方法,也就是说整个流程是这样
- 我们有个接受实例的方法
- 我们将改方法传入Message的ref属性
- Message组件在其实例化时会自动调用这个方法,并且把自身实例传入进去
- 方法内部拿到了实例
// Message.tsx
function addInstance(noticeProps:MessageProps) {
const div = document.createElement('div');
(container || document.body).appendChild(div);
ReactDOM.createRoot(div as HTMLElement).render(
<Message
transitionClassNames={transitionClassNames}
// 这里利用的是ref可以传入回调方法,instance即是Message组件的实例在,Message实例化后,会执行改方法
ref={(instance) => {
// 当方法执行,我们拿到实例,注入到messageInstance中
messageInstance[position] = instance;
// 调用Message实例add方法,也就是把noticeProps这个东西传入到state的Notices中
// 注意这个方法继承BaseNotification
id = messageInstance[position].add(_noticeProps);
}}
/>
)
}
调用Message
为类的静态属性注入方法
在Message内部有各个类型的静态属性,当我们把addInstance这个方法注入到其中,在其他页面引入后,便可直接调用了
// Message.tsx
const messageTypes = ['info', 'success', 'error', 'warning', 'loading', 'normal'];
messageTypes.forEach((type) => {
Message[type] = (noticeProps: MessageProps | string) => {
const props = typeof noticeProps === 'string' ? { content: noticeProps } : noticeProps;
return addInstance({
...props,
type,
});
};
});
// App.tsx
// 直接调用
Message.success({ duration: 2000 ,content: '你好'})
其他小知识
数组转联合类型
// 通过这种方式将数组转换成联合类型
const messageTypes = ['info', 'success', 'error', 'warning', 'loading', 'normal'] as const;
type MessageTypes = typeof messageTypes[number];
相关链接
组合 vs 继承 – Reactzh-hans.reactjs.org/docs/composition-vs-inheritance.html正在上传…重新上传取消
高阶组件 – Reactzh-hans.reactjs.org/docs/higher-order-components.html正在上传…重新上传取消
财神:React 高阶组件 -- 继承的用法3 赞同 · 4 评论文章
React Transition Group (reactcommunity.org)reactcommunity.org/react-transition-group/
秦书羽:前端进阶系列——理解 React Ref11 赞同 · 2 评论文章正在上传…重新上传取消
官方源码
Arco Design - 企业级产品的完整设计和开发解决方案arco.design/react/components/message
本文含有隐藏内容,请 开通VIP 后查看