現在的頁面只有一個產生 SVG 的表單,不過我們還有一個比較簡單的產生 PNG 的表單還沒有做出來,但在做之前,我想先把目前的狀態使用狀態管理工具來管理,這樣之後要新增功能的時候,就不用擔心狀態管理的問題了。
在 React 中,狀態管理工具有很多種,像是 Context、Redux、Zustand 等等,每一種都有各自的優缺點,像是:
| 狀態管理工具 | 優點 | 缺點 | 
|---|---|---|
| Context | 簡單易上手 | 重新渲染效能可能受影響,狀態龐大也不好維護 | 
| Redux | 狀態可預測,強大的開發工具及龐大的生態系 | 對於簡單的專案可能過於複雜 | 
| Zustand | 簡潔靈活的 API 與無需使用 reducer | 相比 Redux 使用人數較少 | 
本來這篇是想用 Redux Toolkit 來做,但平常工作就有在用,自從接觸過 Zustand 之後,覺得非常簡潔清爽,所以這篇還是決定用 Zustand 來做。
首先先安裝 Zustand:
$ npm install zustand
接下來在 app 資料夾下新增一個 store 資料夾,並且新增一個 index.ts 檔案:
import { create } from 'zustand'
interface Store {
  imgSrc: string | null
  setImgSrc: (imgSrc: string | null) => void
}
const useStore = create<Store>((set) => ({
  imgSrc: null,
  setImgSrc: (imgSrc) => set({ imgSrc }),
}))
export default useStore
使用 Zustand 的起手式是使用 create 來建立一個 store,並且定義 store 中的狀態與方法,這邊定義了 imgSrc 這個狀態,並且定義了 setImgSrc 這個方法。因為目前有用到 useState 的部分只有 imgSrc 這個狀態,所以只定義了這個狀態。
Zustand 跟 Redux 的差別在於,Zustand 的狀態跟方法都是在同一個物件中,而且不需要使用 reducer 來更新狀態,只需要直接使用 setXXX 這種方式就可以更新狀態。
接下來就可以在 page.tsx 中使用狀態了:
import useStore from './store'
export default function Home() {
  const { imgSrc, setImgSrc } = useStore()
  // 省略其他程式碼
  const qrType = watch('qrType', 'URL')
  useEffect(() => {
    setImgSrc(null)
  }, [qrType])
  // 省略其他程式碼
  const fetchQrcodeSvg = async (formData: FormInputs) => {
    try {
      const typeMapping: { [key in FormInputs['qrType']]: keyof QrCodeData } = {
        URL: 'url',
        電話: 'phone',
        地址: 'address',
        Email: 'email'
      }
      const dataKey = typeMapping[formData.qrType]
      const data: QrCodeData = {
        [dataKey]: formData.text,
        foreground: formData.qrColor,
        background: formData.qrBgColor,
        dimensions: formData.qrSize
      }
      const response = await generateQrcode.getSvg(data)
      const blob = new Blob([response.data], { type: 'image/svg+xml' })
      const objectURL = URL.createObjectURL(blob)
      setImgSrc(objectURL)
    } catch (_) {
      console.error('Error fetching image:')
    }
  }
  // 省略其他程式碼
  return (
    <main className='flex min-h-screen flex-col items-center justify-between p-24'>
      // 省略其他程式碼
        {imgSrc && Object.keys(errors).length < 1 ? (
          <div className='flex justify-center mt-10'>
            <Image src={imgSrc} width={500} height={500} alt='QR Code Image' />
          </div>
        ) : (
          <p>
            {hasErrors
              ? '請輸入正確的資訊'
              : '請點擊「產生 QR Code」按鈕'}
          </p>
        )}
      </div>
    </main>
  )
}
基本上就是把原本的 useState 換成 useStore,並且把 setImgSrc 這個方法傳給 fetchQrcodeSvg 這個方法,這樣就可以在 fetchQrcodeSvg 中更新狀態了。
這篇就是使用 Zustand 來做狀態管理,雖然目前的專案狀態不多,但是使用狀態管理工具來管理狀態,之後如果要新增狀態的話,也不用擔心狀態雜亂的問題了。