use toast.ts
本地来源:模版/template/raphael-starterkit-v1-main/analysis/06-工具函数/use-toast.ts.md
提示钩子 (hooks/use-toast.ts) 逐行分析
文件概述
这是一个复杂的自定义 React Hook,实现了全局的 Toast 通知系统。它使用 Reducer 模式管理状态,支持多种类型的通知消息,并提供了完整的生命周期管理。
导入语句分析
"use client";
- 指示这是一个客户端组件
- 确保在浏览器环境中执行
import * as React from "react";
- 导入 React 的所有导出
- 使用命名空间导入的方式
import type { ToastActionElement, ToastProps } from "@/components/ui/toast";
- 导入 Toast 组件的类型定义
- 使用
type关键字表示只导入类型
常量定义分析
const TOAST_LIMIT = 1;
const TOAST_REMOVE_DELAY = 1000000;
TOAST_LIMIT: 限制同时显示的 Toast 数量为 1TOAST_REMOVE_DELAY: 设置 Toast 移除延迟时间(非常长的延迟,实际由用户操作控制)
类型定义分析
ToasterToast 类型
type ToasterToast = ToastProps & {
id: string;
title?: React.ReactNode;
description?: React.ReactNode;
action?: ToastActionElement;
};
- 继承基础 ToastProps
- 添加唯一 id 标识
- 可选的标题和描述(支持 React 节点)
- 可选的操作按钮
Action 类型系统
const actionTypes = {
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",
REMOVE_TOAST: "REMOVE_TOAST",
} as const;
- 定义 Action 类型常量
- 使用
as const确保类型推断精确
type ActionType = typeof actionTypes;
- 从常量对象推断类型
type Action =
| {
type: ActionType["ADD_TOAST"];
toast: ToasterToast;
}
| {
type: ActionType["UPDATE_TOAST"];
toast: Partial<ToasterToast>;
}
| {
type: ActionType["DISMISS_TOAST"];
toastId?: ToasterToast["id"];
}
| {
type: ActionType["REMOVE_TOAST"];
toastId?: ToasterToast["id"];
};
- 定义联合类型的 Action
- 每种操作对应不同的数据结构
- 使用
Partial<T>表示部分更新
State 接口
interface State {
toasts: ToasterToast[];
}
- 定义 Toast 状态接口
- 包含 Toast 数组
工具函数分析
ID 生成器
let count = 0;
function genId() {
count = (count + 1) % Number.MAX_VALUE;
return count.toString();
}
- 使用简单的计数器生成 ID
- 防止数值溢出,使用取模运算
- 转换为字符串类型
超时管理
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
const addToRemoveQueue = (toastId: string) => {
if (toastTimeouts.has(toastId)) {
return;
}
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId);
dispatch({
type: "REMOVE_TOAST",
toastId: toastId,
});
}, TOAST_REMOVE_DELAY);
toastTimeouts.set(toastId, timeout);
};
- 使用 Map 管理超时器
- 防止重复添加相同 ID 的超时器
- 延迟移除 Toast,实现淡出效果
- 自动清理超时器引用
Reducer 函数分析
export const reducer = (state: State, action: Action): State => {
switch (action.type) {
case "ADD_TOAST":
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
};
// ... 其他 case
}
};
ADD_TOAST 处理
case "ADD_TOAST":
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
};
- 新 Toast 添加到数组开头
- 使用 slice 限制数量
- 保持状态不可变性
UPDATE_TOAST 处理
case "UPDATE_TOAST":
return {
...state,
toasts: state.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t
),
};
- 根据 ID 更新特定 Toast
- 使用对象展开合并属性
- 保持其他 Toast 不变
DISMISS_TOAST 处理
case "DISMISS_TOAST": {
const { toastId } = action;
// ! Side effects ! - This could be extracted into a dismissToast() action,
// but I'll keep it here for simplicity
if (toastId) {
addToRemoveQueue(toastId);
} else {
state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id);
});
}
return {
...state,
toasts: state.toasts.map((t) =>
t.id === toastId || toastId === undefined
? {
...t,
open: false,
}
: t
),
};
}
- 处理 Toast 关闭请求
- 可以关闭特定 Toast 或全部
- 设置 open 状态为 false
- 添加到移除队列以延迟删除
REMOVE_TOAST 处理
case "REMOVE_TOAST":
if (action.toastId === undefined) {
return {
...state,
toasts: [],
};
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
};
- 从状态中彻底移除 Toast
- 支持移除特定 Toast 或清空全部
- 使用 filter 创建新数组
全局状态管理分析
状态管理
const listeners: Array<(state: State) => void> = [];
let memoryState: State = { toasts: [] };
function dispatch(action: Action) {
memoryState = reducer(memoryState, action);
listeners.forEach((listener) => {
listener(memoryState);
});
}
- 使用观察者模式管理全局状态
- 内存中维护状态(不是组件状态)
- 状态变化时通知所有监听器
Toast 函数
type Toast = Omit<ToasterToast, "id">;
function toast({ ...props }: Toast) {
const id = genId();
const update = (props: ToasterToast) =>
dispatch({
type: "UPDATE_TOAST",
toast: { ...props, id },
});
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
dispatch({
type: "ADD_TOAST",
toast: {
...props,
id,
open: true,
onOpenChange: (open) => {
if (!open) dismiss();
},
},
});
return {
id: id,
dismiss,
update,
};
}
- 创建新的 Toast 实例
- 生成唯一 ID
- 提供更新和关闭方法
- 自动处理 open 状态变化
- 返回控制方法供外部使用
useToast Hook 分析
function useToast() {
const [state, setState] = React.useState<State>(memoryState);
React.useEffect(() => {
listeners.push(setState);
return () => {
const index = listeners.indexOf(setState);
if (index > -1) {
listeners.splice(index, 1);
}
};
}, [state]);
return {
...state,
toast,
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
};
}
状态同步
const [state, setState] = React.useState<State>(memoryState);
- 使用组件本地状态同步全局状态
- 初始化为当前全局状态
监听器管理
React.useEffect(() => {
listeners.push(setState);
return () => {
const index = listeners.indexOf(setState);
if (index > -1) {
listeners.splice(index, 1);
}
};
}, [state]);
- 注册状态变化监听器
- 组件卸载时清理监听器
- 防止内存泄漏
返回值
return {
...state,
toast,
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
};
- 展开状态对象
- 提供 toast 创建函数
- 提供 dismiss 关闭函数
设计模式分析
Reducer 模式
- 使用 Reducer 管理复杂状态逻辑
- 集中处理状态更新
- 便于测试和调试
观察者模式
- 全局状态变化通知所有监听器
- 解耦状态管理和组件
- 支持多个组件同时使用
单例模式
- 全局唯一的状态管理实例
- 跨组件共享状态
- 统一的状态更新入口
工厂模式
- toast 函数创建 Toast 实例
- 封装创建逻辑
- 提供统一的创建接口
最佳实践总结
- 状态管理:使用 Reducer 模式管理复杂状态
- 全局状态:使用观察者模式实现全局状态共享
- 内存管理:正确清理监听器和超时器
- 类型安全:完整的 TypeScript 类型定义
- 用户体验:提供丰富的 Toast 控制方法
- 性能优化:限制 Toast 数量,避免性能问题
- 可扩展性:支持自定义 Toast 内容和操作
使用示例
function MyComponent() {
const { toast } = useToast();
const showToast = () => {
toast({
title: "Success",
description: "Operation completed successfully",
});
};
return <button onClick={showToast}>Show Toast</button>;
}
这个 Hook 实现了一个完整的全局 Toast 通知系统,展示了高级的状态管理技术和设计模式的应用。它是构建用户交互反馈的重要工具。
本文档为站内渲染。原始文件本地路径:saas/source/templates/模版-template-raphael-starterkit-v1-main-analysis-06-工具函数-use--535c47.md(仅本地保留,不入库不部署)