简介
Sonner 是 React 的一个 Toast 组件。
它支持多种类型的提示信息,如成功、错误、信息、警告和加载中,主题定义,可以在页面中显示提示信息。
安装
npm install sonner
# or
yarn add sonner
基本使用
Toaster app.jsx 里,也可以放在 layout.jsx 布局文件里,这样全局都可以使用
import { Toaster } from "sonner";
function App() {
return (
<div className="App">
<Toaster />
</div>
);
}
export default App;
这样组件或者页面,都可以直接使用 toast 方法了
import { toast } from "sonner";
toast.success("Success message");
toast.error("Error message");
toast.info("Info message");
toast.warning("Warning message");
toast.loading("Loading message");
'top','right' | 'bottom' | 'left'
Sonner 参数
|-----------------|--------------|----------------------|-----------|-----------------------------------------------------------------|
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
| position | 位置 | string | top-right | top-left','top-right','bottom-left','bottom-right','top-center' |
| duration | 持续时间 | number | 4000 | |
| theme | 主题 | string | 'ligth | 'light,dark,system' |
| offset | 偏移量 | string,number,object | 32px | |
| mobileOffset | 移动端偏移量 | string,number,object | 16px | |
| expanded | 是否展开 | boolean | false | true,false |
| richColors | 是否显示彩色 | boolean | false | true,false |
| visibleToasts | 显示的 toast 数量 | number | 3 | |
| closeButton | 是否显示关闭按钮 | boolean | false | true,false |
| direction | 方向 | string | 'ltr' | 'ltr','rtl' |
| swipeDirections | 滑动方向 | string | 'up' | 'up','down' |
| hotkeys | 快捷键 | boolean | false | true,false |
| invert | 是否反转 | boolean | false | true,false |
| toastOptions | toast 选项 | object | {} | |
| gap | 间距 | number | 16px | |
| icons | 自定义图标 | object | object | |
Sonner 示例
toastOptions 设置
import { Toaster } from "sonner";
function App() {
return (
<div className="App">
<Toaster
toastOptions={{
style: {
background: "red",
},
className: "my-toast",
classNames: {
toast: "my-toast",
icon: "my-icon",
message: "my-message",
},
descriptionClassName: "my-description",
cancelButtonStyle: React.CSSProperties,
actionButtonStyle: React.CSSProperties,
}}
/>
</div>
);
}
export default App;
更多参数配置,请往下参考 toast 方法参数
icons 自定义
import { Toaster } from "sonner";
function App() {
return (
<div className="App">
<Toaster
icons={{
success: <CheckCircle />,
error: <XCircle />,
warning: <ExclamationCircle />,
info: <InfoCircle />,
loading: <Spinner />,
}}
/>
</div>
);
}
export default App;
toast 参数
|--------------------|------------|-----------|---------|------------|-----------------------------------------------------------------|
| 参数 | 说明 | 类型 | 默认值 | 可选值 | |
| description | 描述 | string | | | |
| icon | 图标 | ReactNode | | | |
| duration | 持续时间 | number | 4000 | | |
| position | 位置 | string | | top-right | top-left','top-right','bottom-left','bottom-right','top-center' |
| closeButton | 是否显示关闭按钮 | boolean | false | true,false | |
| invert | 是否反转 | boolean | false | true,false | |
| dismissible | 是否可手动关闭 | boolean | true | true,false | |
| action | 确认按钮 | ReactNode | | | |
| cancelButton | 取消按钮 | ReactNode | | | |
| id | 唯一标识 | number | string | | |
| onDismiss | 关闭回调函数 | function | ()=>{} | | |
| onAutoClass | 超时自动关闭回调函数 | function | ()=>{} | | |
| containerAriaLabel | 容器 aria 标签 | string | | | |
| actionButtonStyle | 确认按钮样式 | object | | | |
| cancelButtonStyle | 取消按钮样式 | object | | | |
toast 示例
更新 toast
import { toast } from "sonner";
function Demo() {
const updateToast = () => {
const toastId = toast("Sonner");
setTimeout(() => {
toast("updateed toast", {
id: toastId,
});
}, 3000);
};
return (
<div className="demo">
<button onClick={updateToast}>Update Toast</button>
</div>
);
}
export default App;
自定义 Toast 关闭倒计时显示
useCountdown.js
import { useState, useEffect, useCallback } from "react";
/**
* 倒计时 Hook
* @param initialSeconds 初始倒计时秒数
* @param onEnd 倒计时结束回调
* @returns [剩余秒数, 重启倒计时函数]
*/
export function useCountdown(initialSeconds, onEnd) {
const [seconds, setSeconds] = useState(initialSeconds);
// 重启倒计时
const restart = useCallback(
(newSeconds) => {
// 允许动态修改初始时间,默认使用原始初始值
setSeconds(newSeconds ?? initialSeconds);
},
[initialSeconds]
);
useEffect(() => {
if (seconds <= 0) {
onEnd?.();
return;
}
const timer = setInterval(() => {
setSeconds((prev) => {
if (prev <= 1) {
clearInterval(timer);
onEnd?.();
return 0;
}
return prev - 1;
});
}, 1000);
return () => clearInterval(timer);
}, [seconds, onEnd]);
return [seconds, restart];
}
EasySonner.jsx
import { toast } from "sonner";
import { useCountdown } from "useCountdown";
export default function EasySonner({
initialSeconds = 4,
message,
isCountdown = true,
isClose = true,
}) {
const [seconds] = useCountdown(initialSeconds, () => {
toast.dismiss();
});
return (
<div className="flex w-full items-center justify-between">
<div className="flex-1 items-center flex gap-2">
{isClose && (
<i
className="inline-block h-[max-content] w-[max-content] sysicon sysicon-alert-error text-field-error text-[14px]"
onClick={() => {
toast.dismiss();
}}
></i>
)}
{message}
</div>
{isCountdown && <div>{seconds}s</div>}
</div>
);
}
使用
import { toast } from "sonner";
import EasySonner from "EasySonner";
const duration = 10; // 10秒
const promise = new Promise((resolve) => {
setTimeout(resolve, 1000 * duration); // 模拟异步操作
});
toast.promise(promise, {
duration: 1000 * duration,
icon: null, // 不显示loading图标
loading: (
<EasySonner
initialSeconds={duration}
message="Please agree"
isCountdown={true}
isClose={true}
/>
),
});
参数
|----------------|----------|---------|------|
| 参数 | 说明 | 类型 | 默认值 |
| initialSeconds | 初始秒数 | number | 4 |
| message | 提示内容 | string | -- |
| isCountdown | 是否倒计时 | boolean | true |
| isClose | 是否显示关闭按钮 | boolean | true |
