React+TS前台项目实战(十二)-- 全局常用组件Toast封装,以及rxjs和useReducer的使用
文章目录
- 前言
- Toast组件
- 1. 功能分析
- 2. 代码+详细注释
- (1)建立一个reducer.ts文件,用于管理状态数据
- (2)自定义一个清除定时器的hook
- (3)使用rxjs封装全局变量管理hook
- (4)在toast组件中引入上述封装文件
- 3. 使用方式
- 4. toast动画效果展示
- 总结
前言
今天这篇讲的这个组件,是一个用于全局提示的 React灵巧组件。
Toast组件
1. 功能分析
(1)使用 state.toasts 数组和 ToastItem 组件来渲染 toast 消息列表
(2)ToastItem 组件用于渲染单个 toast 消息,并使用渐隐动画
(3)useSetToast 函数返回一个回调函数,用于将 toast 消息设置到全局状态中
(4)组件从全局状态中获取当前的 toast 消息,并使用 useToastData hook 获取状态管理函数
(5)useEffect hook 用于在接收到 toast 消息时将其添加到状态中
(6)当从状态中移除 toast 消息时,会调用 willLeave 函数来更新状态并触发渐隐动画
2. 代码+详细注释
(1)建立一个reducer.ts文件,用于管理状态数据
import { useReducer } from 'react'
export interface ToastMessage {message: stringtype: 'success' | 'warning' | 'danger'duration?: numberid: number
}
interface State {toasts: ToastMessage[]toast: string
}
interface Action {type: 'ADD' | 'REMOVE'payload: {toast: ToastMessage}
}
// 初始状态
const initialState: State = {toasts: [],toast: '',
}
const reducer = (state: State, action: Action) => {switch (action.type) {case 'ADD':return {...state,toasts: state.toasts.concat(action.payload.toast),}case 'REMOVE':return {...state,toasts: state.toasts.filter((toast: ToastMessage) => toast.id !== action.payload.toast.id),}default:return state}
}export const useToastData = () => {const [state, dispatch] = useReducer(reducer, initialState)return {state, dispatch}
}
(2)自定义一个清除定时器的hook
// 定义一个自定义的Hook,用于在组件卸载时清除定时器
// 参数:
// - callback:定时器触发时执行的回调函数
// - clearCallback:定时器卸载时执行的清除回调函数
// - delay:定时器延迟执行的时间
export const useTimeoutWithUnmount = (callback: () => void, clearCallback: () => void, delay: number) => {// 使用useRef保存回调函数和清除回调函数的引用const savedCallback = useRef(() => {})const savedClearCallback = useRef(() => {})// 在组件挂载时注册回调函数和清除回调函数useEffect(() => {savedCallback.current = callbacksavedClearCallback.current = clearCallback})// 在组件挂载和卸载时设置定时器useEffect(() => {// 定义定时器的回调函数const tick = () => {// 执行保存的回调函数savedCallback.current()}// 设置定时器,执行tick函数,并返回定时器的IDconst listener = setTimeout(tick, delay)// 返回一个清除定时器的函数return () => {// 清除定时器clearTimeout(listener)// 执行清除回调函数savedClearCallback.current()}}, [delay])
}
(3)使用rxjs封装全局变量管理hook
import { useObservableState } from 'observable-hooks'
import { Dispatch, SetStateAction, useCallback } from 'react'
import { BehaviorSubject } from 'rxjs'// 全局状态的类型定义
export type GlobalState<T> = BehaviorSubject<T>// 创建一个全局状态
export function createGlobalState<T>(initState: T): GlobalState<T> {return new BehaviorSubject<T>(initState)
}// 设置全局状态的值
export function setGlobalState<T>(globalState: GlobalState<T>, value: T) {globalState.next(value)
}// 获取全局状态的值
export function getGlobalState<T>(globalState: GlobalState<T>): T {return globalState.getValue()
}// 创建一个全局状态的设置函数
export function createGlobalStateSetter<T>(globalState: GlobalState<T>): (value: T) => void {return (value: T) => setGlobalState(globalState, value)
}// 使用全局状态的 hook
export function useGlobalState<T>(globalState: GlobalState<T>): [T, Dispatch<SetStateAction<T>>] {// 通过 useObservableState 获取全局状态的值const state = useObservableState(globalState)// 设置全局状态的值的函数const setState = useCallback<Dispatch<SetStateAction<T>>>((state: T | ((prevState: T) => T)) => {// TODO: 这里使用了 `as` 关键字,因为 `T` 没有约束来禁止状态的函数类型。// 但是实现这种约束会很困难,所以暂时使用 `as` 解决。const finalState =typeof state === 'function' ? (state as (prevState: T) => T)(getGlobalState(globalState)) : stateglobalState.next(finalState)},[globalState],)return [state, setState]
}
(4)在toast组件中引入上述封装文件
// @/components/Toast/index.tsx
import { useState, useEffect, useCallback } from 'react'
import { useTimeoutWithUnmount } from '@/hooks'
import { ToastItemPanel, ToastPanel } from './styled'
import { createGlobalState, useGlobalState } from '@/utils/state'
import { useToastData, ToastMessage } from './reducer'/*** 根据不同的toast类型返回对应的颜色* @param {ToastMessage['type']} type - 类型,可选值为'success'、'warning'、'danger'* @returns {string} - 对应的颜色值*/
const getColor = (type: ToastMessage['type']) => {switch (type) {case 'success':return 'var(--primary-color)'case 'warning':return '#ffae42'case 'danger':return '#D03A3A'default:return '#3cc68a'}
}const ANIMATION_DISAPPEAR_TIME = 2000
const MAX_FRAME: number = (ANIMATION_DISAPPEAR_TIME / 1000) * 40 // suppose fps = 40
const DEFAULT_TOAST_DURATION = 3000// ToastItem 组件用于渲染一个 toast 消息
const ToastItem = ({ data, willLeave }: { data: ToastMessage; willLeave: Function }) => {// 初始化透明度为1const [opacity, setOpacity] = useState(1)let animationId: number = 0// 定义一个定时器,在指定时间后执行 willLeave 函数,实现 toast 消息的逐渐消失效果useTimeoutWithUnmount(() => {const requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFramelet count: number = 0// 定义一个更新透明度的函数,每次调用都会递增 count,并根据 count 的值计算透明度const updateOpacity = () => {count++setOpacity(1 - count / MAX_FRAME)if (count < MAX_FRAME) {requestAnimationFrame(updateOpacity)} else {// 如果执行完一轮动画后,清除定时器willLeave()}}animationId = requestAnimationFrame(updateOpacity)},() => {if (animationId) {const cancelAnimationFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFramecancelAnimationFrame(animationId)}},data.duration || DEFAULT_TOAST_DURATION,)// 渲染 toast 消息return (<ToastItemPanelstyle={{opacity,background: getColor(data.type),}}><div className="toastText">{data.message}</div></ToastItemPanel>)
}// 创建全局状态,用于存储 toast 消息
const globalToast = createGlobalState<ToastMessage | null>(null)// 返回一个函数,用于设置 toast 消息
export function useSetToast() {const [, setToast] = useGlobalState(globalToast)return useCallback((data: Pick<ToastMessage, 'message' | 'duration'> & Partial<Pick<ToastMessage, 'type'>>) =>setToast({id: new Date().getTime(),message: data.message,type: data.type ?? 'success',duration: data.duration,}),[setToast],)
}// Toast 组件是一个提供 toast 消息展示的组件
export default () => {// 获取全局状态中的 toast 消息const [toast] = useGlobalState(globalToast)// 获取 toast 消息的状态和 dispatch 函数const { state, dispatch } = useToastData()useEffect(() => {// 如果 toast 消息不为空,则将其添加到状态中if (toast) {dispatch({type: 'ADD',payload: {toast,},})}}, [dispatch, toast])// 如果状态中没有 toast 消息,则返回 null,否则渲染 toast 消息列表return state.toasts.length === 0 ? null : (<ToastPanel className="toast">{state.toasts &&state.toasts.map((item: ToastMessage) => (// 渲染每个 toast 消息,并在消失后通过 dispatch 函数将其从状态中移除<ToastItemwillLeave={() => {dispatch({type: 'REMOVE',payload: {toast: item,},})}}key={item.id}data={item}/>))}</ToastPanel>)
}
------------------------------------------------------------------------------
// @/components/Toast/styled.tsx
import styled from 'styled-components'
import variables from '@/styles/variables.module.scss'
export const ToastPanel = styled.div`position: absolute;position: -webkit-absolute;top: 0;width: 100%;height: 100%;box-sizing: border-box;display: flex;z-index: 9998;flex-direction: column;pointer-events: none;
`
export const ToastItemPanel = styled.div`width: 100%;position: fixed;position: -webkit-fixed;top: var(--navbar-height);opacity: 0.96;z-index: 9999;height: 60px;.toastText {color: white;font-size: 20px;line-height: 60px;text-align: center;}@media (max-width: ${variables.mobileBreakPoint}) {top: 42px;height: 36px;.toastText {font-size: 14px;line-height: 36px;}}@media (max-width: 320px) {top: 42px;height: 36px;.toastText {font-size: 12px;line-height: 36px;}}
`
3. 使用方式
// 在layout布局文件中使用Toast组件
import Toast from '@/components/Toast'
// 添加到layout布局文件中,具体layout文件代码看:https://blog.csdn.net/weixin_43883615/article/details/139505250
<Page><Header /><Suspense fallback={<span>loading...</span>}><ErrorBoundary><Content><Outlet /></Content></ErrorBoundary></Suspense><Footer /><Toast />
</Page>
------------------------------------------------------------------------------------
// 在需要使用的组件中引入
import { useSetToast } from '@/components/Toast'
// 定义与使用
const setToast = useSetToast()
// 成功效果
setToast({ message: '哦豁弹窗成功了', type: 'success' })
// 警告效果
setToast({ message: '哦豁弹窗成功了', type: 'danger' })
4. toast动画效果展示


总结
下一篇讲【全局常用组件Header封装】。关注本栏目,将实时更新。
相关文章:
React+TS前台项目实战(十二)-- 全局常用组件Toast封装,以及rxjs和useReducer的使用
文章目录 前言Toast组件1. 功能分析2. 代码详细注释(1)建立一个reducer.ts文件,用于管理状态数据(2)自定义一个清除定时器的hook(3)使用rxjs封装全局变量管理hook(4)在to…...
總結光學(完)
參考: 陈曦<<光学讲义>>http://ithatron.phys.tsinghua.edu.cn/downloads/optics.pdf 1 波动光学 最简单的一种波是平面波。........... 一个波的波前是指相位相同的点构成的面。波的传播方向垂直于波面。 我们在此将讨论的光波特指波长远大于原子尺度又远小于…...
线程C++
#include <thread> #include <chrono> #include <cmath> #include <mutex> #include <iostream> using namespace std;mutex mtx; void threadCommunicat() {int ans 0;while (ans<3){mtx.lock();//上锁cout << "ans" <…...
DAMA学习笔记(二)-数据治理
1.引言 数据治理(Data Governance,DG)的定义是在管理数据资产过程中行使权力和管控,包括计划、监控和实施。在所有组织中,无论是否有正式的数据治理职能,都需要对数据进行决策。建立了正式的数据治理规程及…...
07-appium常用操作
一、press_keycode 1)方法说明 press_keycode方法是appium的键盘相关函数,可以实现键盘的相关操作,比如返回、按键、音量调节等等。也可以使用keyevent方法,功能与press_keycode方法类似。 常见按键编码:https://www.…...
使用lua开发apisix自定义插件并发布
接到老大需求:需要对cookie进行操作,遂查询apisix的自带插件,发现有,但不满足,于是自己开发了一个插件并部署,把开发部署流程写在这里打个日志怕以后忘掉。 一、需求 插件很简单,就是在reques…...
43 mysql insert select 的实现
前言 我们这里 来探讨一下 insert into $fields select $fields from $table; 的相关实现, 然后 大致来看一下 为什么 他能这么快 按照 我的思考, 应该里里面有 批量插入才对, 但是 调试结果 发现令我有一些意外 呵呵 果然 只有调试才是唯一的真理 测试数据表如下 CREATE…...
趣味学Python,快速上手神奇的itertools库!
大家好,我是菜哥! 在学习Python编程的过程中,我们经常会使用到一些非常有用的标准库,它们不仅可以让我们的代码更加简洁高效,还能帮我们解决很多复杂的问题。Python标准库为我们提供了大量实用的工具和模块,…...
富文本编辑器CKEditor
介绍 富文本编辑器不同于文本编辑器,它提供类似于 Microsoft Word 的编辑功能 在Django中,有可以现成的富文本三方模块django-ckeditor,具体安排方式: pip install django-ckeditor==6.5.1官网:Django CKEditor — Django CKEditor 6.7.0 documentation 使用方式 创建项…...
【机器学习】音乐大模型的深入探讨——当机器有了创意,是机遇还是灾难?
👀国内外音乐大模型基本情况👀 ♥概述♥ ✈✈✈如FreeCompose、一术科技等,这些企业专注于开发人工智能驱动的语音、音效和音乐生成工具,致力于利用核心技术驱动文化产业升级。虽然具体公司未明确提及,但可以预见的是…...
机器人学习和研究的物质基础包含哪些内容?
为啥写这个? 在很多博客里面提及物质基础,没想到询问的也非常多,写一篇详细一点的。 之前的故事 不合格且失败机器人讲师个人理解的自身课程成本情况-CSDN博客 迷失自我无缘多彩世界-2024--CSDN博客 物质基础与情绪稳定的关系-CSDN博客 …...
Python中的交互式GUI开发:与MATLAB uicontrol的比较
Python中的交互式GUI开发 Python中的交互式GUI开发:与MATLAB uicontrol的比较**Python GUI开发库****Tkinter****PyQt/PySide** **与MATLAB的比较****总结** Python中的交互式GUI开发:与MATLAB uicontrol的比较 在MATLAB中,uicontrol 是一个…...
js 实现将后端请求来的 Blob 数据保存到用户选择的任意目录
js实现将后端请求来的 Blob 数据保存到用户选择的任意目录 实现方式 实现方式 实现方式是使用 window 的 showSaveFilePicker 方法。Window 接口的 showSaveFilePicker() 方法用于显示一个文件选择器,以允许用户保存一个文件。可以选择一个已有文件覆盖保存…...
【LLM之RAG】RAT论文阅读笔记
研究背景 近年来,大型语言模型(LLMs)在各种自然语言推理任务上取得了显著进展,尤其是在结合大规模模型和复杂提示策略(如链式思维提示(CoT))时。然而,LLMs 在推理的事实…...
windows anaconda 安装 Labelme
安装 # 创建环境 conda create -n labelme python3.6 #激活环境 conda activate labelme # 安装依赖 conda install pyqt conda install pillow # 安装labelme conda install labelme3.16.2 # 启动labelme labelme右键选择标注类型,从上到下为多边形(常…...
Python实现基于深度学习的电影推荐系统
Python实现基于深度学习的电影推荐系统 项目背景 在数字化娱乐时代,用户面临着海量的电影选择。为了帮助用户找到符合个人口味的佳片,MovieRecommendation项目提供了一个基于深度学习的个性化电影推荐系统。该系统利用深度学习技术,根据用户…...
C++ (week9):Git
文章目录 1.git介绍2.git安装3.git配置4.获取自己的SSH公钥5.新建仓库6.邀请开发者7.克隆远程仓库到本地8.在本地进行开发9.本地项目推送到远程仓库10.git的工作原理11.分支管理(1)合作开发的方式(2)分支管理(3)分支合并的原理、冲突管理 12.git 与 svn 的区别13.设置alias别名…...
Seaborn:数据可视化的强大工具
文章目录 引言Seaborn的原理1. 底层结构2. 数据集成3. 图形类型 Seaborn的使用1. 安装与导入2. 数据加载与探索3. 绘制图形分布图关系图分类图 4. 图形定制5. 导出图形 结论 引言 在数据分析和科学计算领域,数据可视化是一个至关重要的步骤。它能够帮助我们更直观地…...
图解注意力
图解注意力 Part #2: The Illustrated Self-Attention 在文章前面的部分,我们展示了这张图片来展示自注意力被应用于正在处理单词"it"的一层中: 在本节中,我们将看看这是如何完成的。请注意,我们将以一种试图理解单…...
Typora Markdown编辑器 for Mac v1.8.10 安装
Mac分享吧 文章目录 效果一、准备工作二、开始安装1、双击运行软件,将其从左侧拖入右侧文件夹中,等待安装完毕2. 应用程序显示软件图标,表示安装成功 三、运行调试1、修改主题2、显示文档列表,如下图3、查看版本信息 **安装完成&…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
