iT邦幫忙

2023 iThome 鐵人賽

DAY 18
2
SideProject30

營養師不開菜單要用 Next.js 13 寫全端系列 第 18

營養師不開菜單的第十八天 - 使用 React-Hot-Toast 實現各種狀態通知

  • 分享至 

  • xImage
  •  

https://github.com/timolins/react-hot-toast/raw/main/assets/header.svg

react-hot-toast 是一個專為 React 設計的輕量級通知提示(toast)套件。預設有成功、失敗、loading(Promise) 三種類型的提示功能,當然也可以透過屬性簡單的自訂義提示樣式、情境及通知訊息文字。同時,開發者也是目前 Vercel Analytics 的開發作者群之一,本身除了開發套件也是 UI 設計師,在套件官網和 toast 元件設計上都有很好的視覺體驗!

為什麼要用 react-hot-toast?


  1. 簡單 API 設計:其 API 設計得非常直觀,只需要在應用程式中設置 Toast 元件,再於需使用時調用 toast() 即可。

  2. 可客製化姓:雖然預設樣式已經很美了,但 react-hot-toast 依舊提供自訂義方法,可以修改 toast 的外觀和功能。

  3. 不僅僅是文字:除了顯示純文字的 toast,也可以是有 icon 的 toast 甚至是自定義的 React 組件。

  4. 支援 Promise 通知:可以顯示 loading 到成功或失敗的狀態。

  5. 無障礙性react-hot-toast 考慮到了無障礙性,確保所有使用者都能獲得良好的體驗。

  6. 內建排隊系統:當多個 toast 通知同時觸發時,它們會自動排隊,而不是同時顯示,這可以避免用戶界面上的混亂。

  7. 輕量型套件:在 minified 和 gzipped 後的大小只有 4.8kb。

    https://ithelp.ithome.com.tw/upload/images/20230930/20152073gwzwqSBehP.png

開始設置


一進入官網有快速建置三步驟:安裝 → 設置 Toaster → 調用 toast() ,所以接下來就先依照這個步驟建置一個基本的 toast 吧!

安裝

npm i react-hot-toast

設置 Toaster

整個應用程式只需要設置一個 Toaster,在需要時以 toast() 調用,所以這個 Toaster 就必須設置在比較高的層級。在 Next.js 專案中,會設置在 root layout 中,這邊要注意的是,Toaster 是一個 Client Component 的 Provider 類型套件,所以會先將這個套件封裝為 ToastProvider ,再放置於 root layout。

// ToasterProvider.tsx

'use client'

import { Toaster } from 'react-hot-toast'

const ToasterProvider = () => {
  return <Toaster />
}

export default ToasterProvider

接下來匯集到 Providers 中,再將 Providers 設於 root layout

// providers/Providers.tsx

'use client'

import React, { FC } from 'react'
import { ThemeProvider } from 'next-themes'

import { type ThemeProviderProps } from 'next-themes/dist/types'
import ToasterProvider from './ToastProvider'

const Providers: FC<ThemeProviderProps> = ({ children, ...props }) => {
  return (
    <ThemeProvider {...props}>
      <ToasterProvider />
      {children}
    </ThemeProvider>
  )
}

export default Providers

調用 toast()

在需要的地方調用,例如 API 回傳成功後調用 toast()

https://ithelp.ithome.com.tw/upload/images/20230930/20152073OEV0WEOFue.png

import { toast } from 'react-hot-toast'

const onSubmit = async (values: FormValues) => {
  try {
    const { data: res } = await axios.put(`/api/user/${user?.id}`, values)
    update({ admin: res.data })
    toast.success('更新成功')
  } catch (error) {
    toast.error('更新失敗')
    console.log(error)
  }
}

各式各樣的使用方法


react-hot-toast 的建置非常簡單,當然也可以依據不同的場景進行客製化設定或其他操作,其中預設就有不同狀態的顯示功能。

有哪些狀態

調用 toast() 可以設置最基本,無 icon 單純文字的樣式

https://ithelp.ithome.com.tw/upload/images/20230930/20152073W8uUud6exy.png

成功狀態

使用 success 選項,會預設綠色勾勾 icon

https://ithelp.ithome.com.tw/upload/images/20230930/20152073OEV0WEOFue.png

toast.success('更新成功')

失敗/錯誤狀態

使用 error 選項,會預設紅色叉叉 icon

https://ithelp.ithome.com.tw/upload/images/20230930/20152073Z2runIo6aR.png

toast.error('更新失敗')

Loading 狀態

使用 loading 選項,會預設轉圈動畫,沒有時間長度設定。

https://ithelp.ithome.com.tw/upload/images/20230930/20152073QziZk4b7X0.png

toast.loading('更新中...')

Promise 狀態

使用 promise 選項,調用時需傳入兩個參數,第一個參數是 promise 的 function,第二個則是可以設定 loading、success、error 三個狀態時需要呈現的內容

https://imgur.com/v8YVrso

	toast.promise(
      axios.put(`/api/user/${user?.id}`, values).then(({ data: res }) => {
        update({ admin: res.data })
      }),
      {
        loading: '更新中...',
        success: '更新成功',
        error: '更新失敗'
      }
    )

客製化 Toast 的屬性

Toast 有提供樣式、位置、 toast 間間隔等,而各種狀態 toast 也可以設定其樣式、顯示時間長度、id、icon、className、style 等等。

https://imgur.com/EGlXoea

const ToasterProvider = () => {
  return (
    <Toaster
      position="top-right"
      gutter={20}
      reverseOrder={false}
      containerStyle={{
        position: 'relative',
        top: '5rem',
        marginRight: '1.5rem'
      }}
      toastOptions={{
        loading: {
          icon: '👏',
          style: {
            background: '#CEE0FD',
            fontWeight: 'bold'
          }
        }
      }}
    />
  )
}

還想客製化 toast 樣式

除了上面預設的各種狀態 toast,react-hot-toast 也提供客製化整個 toast 的屬性,可以使用 custom 屬性,在參數中可回傳一個 React Element 並且接收一個 toast props 可以調用 toast 功能,另外也支援 Tailwind CSS 的樣式設定。

https://ithelp.ithome.com.tw/upload/images/20230930/20152073iC5fTwPUpQ.png

toast.custom((t) => (
        <div
          className={`${
            t.visible ? 'animate-enter' : 'animate-leave'
          } max-w-md w-full bg-white shadow-lg rounded-lg pointer-events-auto flex ring-1 ring-black ring-opacity-5`}
        >
          <div className="flex-1 w-0 p-4">
            <div className="flex items-start">
              <div className="flex-shrink-0 pt-0.5">
                <Image
                  className="h-10 w-10 rounded-full"
                  src={placeholder}
                  alt=""
                />
              </div>
              <div className="ml-3 flex-1">
                <p className="text-sm font-medium text-gray-900">Renee Lan</p>
                <p className="mt-1 text-sm text-gray-500">
                  要遲到了,太陽都要曬屁股了!
                </p>
              </div>
            </div>
          </div>
        </div>
      ))

可以關掉整個通知訊息嗎?

react-hot-toast 提供一個屬性可以調用後關閉通知 - toast.dismiss()

如果是 custom 的 callback 中調用,可以傳遞 toast id 指定該 toast 關閉,也可以單純調用 toast.dismiss() ,但有多個時就會關閉所有通知

https://imgur.com/xF3g9qJ

<div className="flex border-l border-gray-200">
   <button
     onClick={() => toast.dismiss()}
     className="w-full border border-transparent rounded-none rounded-r-lg p-4 flex items-center justify-center text-2xl font-medium text-primary-600 hover:text-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-500"
   >
     <AiOutlineCloseCircle />
   </button>
</div>

還有更細膩的客製化方法

上述的功能其實已經可以滿足大部分需求,但 react-hot-toast 還提供了一個可以控制更細部功能的 hook - useToaster :包含 toastshandlers 等回傳值。

  • toasts - 為當前所有活躍中 toast 的陣列
  • handlers - 包含了 startPause, endPause, calculateOffset, 和 updateHeight 等方法的物件
    • startPause:暫停任何新的 toast 顯示。如果有排隊的 toast,它們將不會顯示,直到 endPause 被調用
    • endPause:結束暫停,允許 toast 再次顯示
    • calculateOffset:計算特定 toast 的偏移量
    • updateHeight:更新特定 toast 的高度

結語

一個小小的通知功能也是有很大的學問,個人最喜歡的是 Promise 功能,可以自動幫你捕捉 loading 到最後結果的狀態,也不需要太冗長的設定,是個非常實用的功能。另外 Issue 中有看到其他使用者希望可以像其他套件一樣加上進度條功能,關於這點作者表示進度條在這個通知功能上不是非常必要,但依舊可以使用客製化的方法自己加上!

https://ithelp.ithome.com.tw/upload/images/20230930/20152073INNYlN5fYM.png

https://ithelp.ithome.com.tw/upload/images/20231001/20152073b8v3nzFNlS.png


上一篇
營養師不開菜單的第十七天 - 在 Next.js 中使用 Cloudinary CDN 管理媒體資源
下一篇
營養師不開菜單的第十九天 - Next.js 實作 - Link In Bio Tool 畫面設計與整體規劃
系列文
營養師不開菜單要用 Next.js 13 寫全端30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言