iT邦幫忙

2025 iThome 鐵人賽

DAY 15
0
Modern Web

JavaScript 進階修煉與一些 React ——離開初階工程師新手村的頭30天系列 第 15

離開 JS 初階工程師新手村的 Day 15|狀態商店藍圖:Zustand 模板

  • 分享至 

  • xImage
  •  

Zustand 帶來更好的實時性、更精準的狀態更新管理、以及更集中、更好閱讀的 code。

在前幾天的文章掌握了 Zustand 的基本概念後,實際開發中我們會面臨新的挑戰:如何標準化非同步操作?如何確保團隊使用一致的模式?

回顧

在深入模板之前,讓我們快速回顧基本的 Zustand Store

https://ithelp.ithome.com.tw/upload/images/20250926/20168365qin6pGwFwE.png

雖然 Zustand 非常簡潔,但在企業級應用中會遇到一些挑戰:

  1. 異步狀態管理的重複模式:每個非同步操作都需要手動管理 loading、success、error 狀態
  2. 選擇器的冗 codeuseStore(state => state.property) 模式重複出現
  3. 缺乏標準化結構:團隊成員可能採用不同的異步處理方式以及複雜的異步操作 Class 推導困難

模板架構

因此 DO RE MI SO ~! 這個進階模板採用了三層架構設計,每一層都有明確的職責,以解決以上的問題。

┌─────────────────┐
│   Store Layer   │  ← 狀態定義與組合
├─────────────────┤
│  Actions Layer  │  ← 業務邏輯與異步操作
├─────────────────┤
│   Types Layer   │  ← 類型定義與約束
└─────────────────┘

Types Layer

https://ithelp.ithome.com.tw/upload/images/20250926/20168365OZmu9KgUmm.png

雖然 JS 在型別上比較模糊,但只要先定義好,就可以在方法的參考上顯示參數有哪些、型別是什麼,進而定義了整個系統的數據結構。雖然看起來簡單,但它為整個模板提供了型別安全的保障。

Actions Layer

這是整個模板最創新的部分,它為非同步操作提供了兩種不同層次的抽象,前者使用 makeAction 包裝,可以自己控制內部複雜的邏輯,無論是要用 get 從 store 拿狀態,或是以非同步的 return 值再去做操作都可以。

makeAction

https://ithelp.ithome.com.tw/upload/images/20250926/20168365Z5bwOoVA8q.png

非同步的操作已知狀態只有三種: loading, resolved, rejected,剛好對應到模板裡面的行為。

初始狀態 → 載入中 → 成功/失敗 → 可選重置
   ↓         ↓        ↓           ↓
 idle    loading   success     idle
                   failed

首先將狀態定義為 loading,等到非同步結束行為回傳值後,再去看是否有成功,然後更新相對應的狀態。

autowrap

那你可能又會說,這段程式還是太長了,跟 Context API 相比,根本沒有比較短。既然已知非同步的操作已知狀態只有三種,那只需要將結果在操作結束時指定即可,在沒有複雜邏輯的情況下, autoWrapper 所做的就是給 resources.getSomeData,也就是對後端的 API 給包上一層,用以管理前端狀態更新。

https://ithelp.ithome.com.tw/upload/images/20250926/20168365cHUpYPMRlV.png

autowrap 是最簡潔的方式,適合簡單的 API 調用場景。

Store Layer

https://ithelp.ithome.com.tw/upload/images/20250926/20168365hLWXVzFnka.png

這一層負責將所有組件組合起來集中管理,並通過 createSelectors 提供增強的使用體驗,使用的時候只需要 call useTemplateStore

比較

在原生 Zustand 中,我們需要這樣使用:

const attribute1 = useStore(state => state.attribute1)
const attribute2 = useStore(state => state.attribute2)
const setAttribute1 = useStore(state => state.setAttribute1)

createSelectors 為每個屬性自動生成專用的 hook:

const attribute1 = useTemplateStore.use.attribute1()
const attribute2 = useTemplateStore.use.attribute2()
const setAttribute1 = useTemplateStore.use.setAttribute1()

// 仍然支援原生方式
const attribute1 = useTemplateStore(state => state.attribute1)

因此我們達到前幾天所說的,粒度的決定權在開發者上,因為每個選擇器只訂閱特定的狀態片段。


接著,在主程式或組件中使用,也可以更好掌握目前非同步行為的狀態,以追蹤是否要狀態改變時相對應的前端畫面。

// 假設有一個動作叫 fetchUserData
// 模板會自動創建:

const isLoading = useTemplateStore.use.is.fetchUserData.loading()
const isSuccess = useTemplateStore.use.is.fetchUserData.success()
const isFailed = useTemplateStore.use.is.fetchUserData.failed()
const error = useTemplateStore.use.is.fetchUserData.error()

這裡想要傳達的概念是異步動作狀態是持久的。這意味著成功的操作會保持成功狀態,直到被明確重置,因此狀態的改變時機可以被清楚的定義,無論是單一組件或是跨域的操作,狀態都不會丟失。

一個很實際的情境,比如使用者搜尋了某些關鍵字而觸發非同步搜尋,即便他之後跳轉了頁面,因為搜索結果會保存在全域狀態中,當使用者再回來,搜索結果依然會存在。


這個模板最強大的特性之一是全域狀態的自動同步與響應式更新。無論用戶在應用的哪個頁面進行操作,因為資料都來自同一個 store,相關的狀態變化都會即時反映到所有訂閱該狀態的組件中。

https://ithelp.ithome.com.tw/upload/images/20250926/20168365UgTUMZYnSN.png

範例

https://ithelp.ithome.com.tw/upload/images/20250926/20168365uVAJJ4I2wD.png

這種全域狀態管理模式特別適合現代 SPA 應用,其中使用者經常在不同頁面間切換,但需要保持一致的數據體驗。模板確保了無論用戶在哪裡進行操作,相關的 UI 元素都能即時響應,提供流暢的用戶體驗。


上一篇
離開 JS 初階工程師新手村的 Day 14|Redux 與 Zustand:不同魔法學派的選擇
系列文
JavaScript 進階修煉與一些 React ——離開初階工程師新手村的頭30天15
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言