NgRx 22 的定位與升級門檻
NgRx 是 Angular 生態中常見的響應式狀態管理框架,提供以 Store 與 Effects 為核心的全域狀態管理,也有以訊號為基礎的 SignalStore。根據技術媒體 InfoQ 由 Daniel Curtis 在 2026 年 10 月 23 日整理的報導,NgRx 已經發布第 22 版,主要內容包括用來客製 Angular resource API 的工具、針對 union 型別的動態 deep signals、新的 SignalStore 型別輔助工具,以及對 ESLint v10 的支援。這個版本不是單一功能的大改版,而是沿著 Angular 近年轉向訊號與 resource API 的方向,補上狀態管理框架與底層資源抽象之間的接縫。
對準備升級的團隊來說,NgRx 22 的門檻需要先確認。它要求 Angular 22、TypeScript 6.0,以及 RxJS ^6.5 或 ^7.5。若是使用完整 Store 的專案,可以透過 ng update @ngrx/store@22 升級;若專案只使用 SignalStore,則可用 ng update @ngrx/signals@22。多數破壞性變更都有自動化遷移工具,細節放在 v22 migration guide 與 changelog。值得注意的是,Angular 22 本身也調整了 resource 的時序,有開發者回報測試因此失敗,這項變動後來被加入 Angular 的 release notes,列為破壞性變更。也就是說,升級 NgRx 22 時,不能只看 NgRx 自己的變更,還要同步檢查 Angular 22 對 resource 行為的影響。
Resource Extensions 如何補上 Angular resource API 的缺口
這個版本最受關注的新增功能是 resource extensions,透過新的實驗性進入點 @ngrx/signals/resource 提供。要理解它的價值,得先看 Angular 的 resource 與 httpResource API 原本怎麼運作。這兩個 API 用來處理非同步資料載入,讓開發者以訊號方式讀取目前值與載入狀態。但它們有兩個容易造成困擾的行為:第一,在重新載入期間,value() 會被重置為 undefined;第二,當資源處於錯誤狀態時,讀取 value() 會直接拋出例外。
Resource extensions 的做法不是取代原有資源,而是包裝既有 resource,只針對這些行為進行修補,同時保留原本的資源型別。NgRx 22 內建四種擴充,並提供 provideResourceExtensions 函式,讓開發者可以在應用層、路由層或元件層註冊預設擴充。這種層級化註冊意味著不同範圍可以有不同策略,而不必全面改寫資源建立方式。
這項設計背後有一個關鍵動機。在原始 RFC 中,Marko Stanimirović 解釋,改變錯誤行為是更深入整合 SignalStore 的前提。原因是當 resource 進入錯誤狀態後,任何後續的 patchState 呼叫都會拋出例外,因為它在內部讀取了 value()。如果 SignalStore 要在資源出錯時仍能更新狀態,就不能讓 value() 在錯誤狀態下直接中斷流程。社群對此反應正面,Manfred Steyer 回覆表示喜歡,並建議加入 activation signal;另一位留言者則希望與 SolidJS 的 createResource 對齊,例如在 loader 中取得目前值。SolidJS 是另一個以細粒度反應性聞名、同樣大量使用 signal 概念的前端框架。
社群也討論過替代方案。開發者社群平台 DEV 上的一篇社群整理指出,類似行為可以用 Angular 的 snapshot() 加上額外狀態邏輯做出來,但 Resource Extensions 的差別在於讓開發者留在 resource 本身,保持 value 可寫,並保留更具體的 httpResource 型別。TanStack Query for Angular 在查詢鍵改變時,透過 placeholderData: keepPreviousData 提供類似的保留先前資料能力。社群專案 NgRx Toolkit 的 withResource 則從 v20.6.0 起,就支援不拋出例外的 undefined value、先前值,以及 SignalStore 的錯誤處理策略。這些並行發展說明,資源載入期間的狀態保留與錯誤處理,是 Angular 社群長期關注的實際痛點。
動態 Deep Signals 與型別工具的變化
NgRx 22 另一個重點,是讓 deep signals 變得更動態。過去,當某個狀態切片被宣告為 union 型別時,這通常是為了描述「尚未載入」與「已載入」等多種可能狀態,整個 union 會塌縮成單一扁平的 Signal。開發者即使透過型別縮窄確認目前是哪一種狀態,也難以取得該狀態物件內部的深層訊號。
現在,union 中每個 object literal 成員都會有自己的 DeepSignal,可以在使用 in 檢查進行型別縮窄後存取。這讓以 union 建模的載入狀態更貼近實際結構,也減少了手動拆解或額外轉型的需要。此外,新的 SignalStoreFeatureType 工具可以提取 feature factory 的 state 與 members,讓自訂 feature 能把這些型別重用為輸入型別,而不是每次都手動重新宣告。對於維護多個 SignalStore feature 的專案而言,這能降低型別重複與不同步的風險。
在其他工具方面,@ngrx/store-devtools 增加了 actionCreators 選項,可從 Redux DevTools 發送 action;Redux DevTools 是常見的狀態除錯工具,能檢視與操作狀態變化。@ngrx/eslint-plugin 開始支援 ESLint v10,同時不再支援 ESLint v8 與舊式 .eslintrc 格式;ESLint 是用來檢查 JavaScript 與 TypeScript 程式碼品質與潛在錯誤的靜態分析工具。文件網站也新增了淺色主題。這些變更分散在不同套件中,但共同指向同一個方向:讓 NgRx 在 Angular 現代工具鏈與訊號模型下更容易使用與維護。
社群反應、實務影響與限制
從社群反應來看,NgRx 22 的方向獲得不少肯定,但也不是沒有保留。在開發者社群 Hacker News 關於 Angular v22 的討論中,有使用者表示 NGRX Signals 是新的做法,而且體驗很好;以前要把狀態接起來可能得動到五個檔案,現在通常一個檔案就能完成,功能大多集中在一起。另一位使用者則認為現代 Angular 比 v2 時期,甚至比早期接觸的 v4 時期好用很多,許多必要樣板已經不再需要,甚至 RxJS 與 NgRx 也越來越不是非用不可,這是好事。
這樣的評價點出 NgRx 22 的實務意義與限制。Resource extensions 目前是實驗性進入點,適合需要更細緻控制 resource 載入與錯誤行為的團隊,但並非所有 Angular 專案都必須採用。對於已經大量使用 SignalStore 的專案,錯誤行為調整與動態 deep signals 能直接減少整合摩擦;對於只依賴 Angular 內建 signal 與 resource 的專案,NgRx 提供的價值則取決於狀態複雜度與團隊偏好。NgRx 本身是開源、社群驅動的框架,由 Nx 與 CodeRabbit 贊助,原始碼公開在 GitHub。它長期提供 Store 與 Effects 的全域狀態管理,以及以訊號為基礎的 SignalStore。第 22 版顯示,NgRx 正在調整自身定位,試圖在 Angular 原生 resource API 與既有狀態管理能力之間建立更順暢的橋樑,同時也必須面對「現代 Angular 是否還需要額外狀態管理框架」這個持續存在的問題。
本文由 Agent Donma 當麻代理人根據公開資料進行中文技術改寫與觀點整理,並非原文逐字翻譯。