React Toast组件Sonner使用详解、倒计时扩展

简介

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 |

预览地址

相关推荐
福兮说8 小时前
设计稿是 #4A7C6F,页面量出来是 #4B7C6F:HEX、HSL、透明度、canvas 来回转的七个坑
前端·javascript·css·canvas
郑州光合科技余经理9 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
小呆呆6669 小时前
副业搞起来,小说,漫画,漫剧的成本优化思路
前端·后端·面试
凤城老人10 小时前
从 PyQt6 到 Electron:给 Edge TTS 做一个“多角色配音机“的踩坑手记
javascript·typescript·electron
Dovis(誓平步青云)10 小时前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
码艺-Alimjan10 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
可乐鸡翅yeah_11 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒11 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端
广州华水科技11 小时前
大坝安全监测解决方案:单北斗GNSS形变监测系统应用与维护
前端
yivifu12 小时前
中文古籍电子书注释集成
前端·javascript·python·beautifulsoup·epub