NgRx Toolkit v21

发布: (2026年1月20日 GMT+8 01:20)
8 min read
原文: Dev.to

Source: Dev.to

Toolkit 提供的功能

NgRx Toolkit 是一套在 Angular 应用中常用的丰富扩展。以下是其核心功能的简要历史:

功能描述
withDevtools()允许任何 SignalStore(无论是否基于 Redux)使用 Redux DevTools。添加 withDevtools('storeName') 即可可视化 store 状态。
@ngrx/signals/event & withFeature现已成为 @ngrx/signals 核心的一部分;最初在 Toolkit 中孵化。
withStorageSync()将状态同步到 Web Storage(localStorage / sessionStorage)和 IndexedDB(通过异步策略)。使用 withStorageSync('storeName')。IndexedDB 支持是去年由社区贡献者(GitHub 用户 mzkmnk)通过 PR #134 添加的。
其他扩展请参阅文档中的完整列表。

NgRx Toolkit extensions

v20 次要特性:withResourcewithEntityResources 与 Mutations

withResource() / withEntityResources()

  • withResource 将 Angular 的 Resource API 与 store 关联,使 store 能够管理异步数据(例如从 API 加载)。它同时支持未命名和已命名两种变体。
  • withEntityResources 为使用 @ngrx/signals/entities 构建的 store 提供相同的功能。

Mutations API

Mutations API 为典型的 REST 交互添加了“写”操作。它提供:

  • 独立函数:httpMutationrxMutation
  • 功能包装器:withMutations

该设计受 Angular QueryMarko Stanimirović 提出的 Mutations API 启发,内部讨论中还涉及 Alex Rickabaugh。

import {
  httpMutation,
  rxMutation,
  withMutations,
  withResource,
  withEntityResources,
} from '@angular-architects/ngrx-toolkit';

export const UserStore = signalStore(
  withState({ userId: undefined as number | undefined }),

  // Async data handling via Angular Resource API
  withResource(({ userId }) => ({
    detail: httpResource(() =>
      userId === undefined ? undefined : `/user/${userId}`
    ),
  })),

  // Mutations (save, post, etc.)
  withMutations((store, userService = inject(UserService)) => ({
    saveUserDetail: rxMutation({
      operation: (params: Params) =>
        userService.saveUserDetail(store.counter(), params.value),
      onSuccess: (result) => {
        // …
      },
      onError: (error) => {
        // …
      },
    }),

    saveToServer: httpMutation({
      request: () => ({
        url: `https://httpbin.org/post`,
        method: 'POST',
        body: { counter: store.counter() },
      }),
      parse: (response) => response as UserResponse,
      onSuccess: (result) => {
        // …
      },
      onError: (error) => {
        // …
      },
    }),
  })),

  // Entity‑level resources
  withEntityResources(() =>
    resource({
      loader: () => Promise.resolve([] as User[]),
      defaultValue: [],
    })
  )
);

v21 – 新功能

主要新增

  1. 改进的错误处理,适用于 withResource()withEntityResources()
  2. 事件集成 到 Redux DevTools
  3. 引入 clearUndoRedo(取代 store.clearStack

升级的 withResource() / withEntityResources() 错误处理

Angular 资源可能会进入死锁状态:一旦资源处于错误状态,更新 params 中的信号会触发 patchState,而 patchState 再次访问状态值,可能导致再次出错。

工具包现在提供了多种处理 withResource() 中错误的策略:

type ErrorHandlingStrategy =
  | 'throw'    // 重新抛出原始错误(默认)
  | 'ignore'   // 静默忽略错误并保留之前的状态
  | 'fallback' // 通过用户提供的函数提供回退值
  | 'custom'   // 执行自定义错误处理回调

在配置资源时选择所需的策略:

withResource(
  ({ userId }) => ({
    detail: httpResource(() =>
      userId === undefined ? undefined : `/user/${userId}`
    ),
  }),
  {
    errorHandling: 'fallback',
    fallbackValue: () => ({ name: 'Anonymous', id: 0 })
  }
);

事件集成到 DevTools

withDevtools() 现在会捕获从 store 发出的自定义事件,使你能够在 Redux DevTools UI 中与状态变化一起查看这些事件。

clearUndoRedo

之前的 store.clearStack() 方法已被 clearUndoRedo() 取代,后者更直观地表达了清除撤销和重做历史的目的。

参考文献

  • 文档: (link to official docs)
  • IndexedDB 贡献: (by mzkmnk)
  • Angular Query(变更灵感): (link)
  • Marko Stanimirović 的变更提案: (link)

错误处理策略

errorHandling = 'native' | 'undefined value' | 'previous value';

withResource(
  (store) => {
    const resolver = inject(AddressResolver);
    return {
      address: resource({
        params: store.id,
        loader: ({ params: id }) => resolver.resolve(id),
      }),
    };
  },
  // Other values: 'native' and 'previous value'
  { errorHandling: 'undefined value' } // default if not specified
);

选项

描述
'undefined value'(默认)当出现错误时,资源的值变为 undefined
'previous value'如果资源之前已有值,则返回该值;否则抛出错误。
'native'不进行特殊处理——使用默认的错误行为。

对于 withEntityResources(),策略为 'undefined value'

在内部实现中,'previous value''undefined value' 会对值进行代理。有关详细说明,请参阅 错误处理策略的 JSDoc

将事件集成到 DevTools

这里有点讽刺意味:NgRx Toolkit 在官方插件出现之前就已经在 Signal Store 中引入了 events,并且它也提供了 Redux DevTools 集成(无论是否使用 Redux)。然而,现已正式的 NgRx events 功能并没有直接映射到 Toolkit 的 withDevtools

NgRx Toolkit v21 中,我们通过 withTrackedReducer() 解决了这个问题,这是一种在 Redux DevTools 中跟踪基于 reducer 的状态变化的替代方式。

使用方法

  1. withReducer 的使用替换为 withTrackedReducer
    (原生 withReducer 支持计划中,但需要在 @ngrx/signals 上游进行更改。)

  2. withDevtools 内部指定 withGlitchTracking
    如果在没有 DevTools 和 glitch tracking 的情况下使用 withTrackedReducer,运行时将抛出错误。

import {
  withTrackedReducer,
  withGlitchTracking,
  withDevtools,
} from '@angular-architects/ngrx-toolkit';

export const bookEvents = eventGroup({
  source: 'Book Store',
  events: {
    loadBooks: type(),
  },
});

const Store = signalStore(
  { providedIn: 'root' },
  withDevtools('book-store-events', withGlitchTracking()),
  withState({
    books: [] as Book[],
  }),
  withTrackedReducer(
    // `[Book Store] loadBooks` 将出现在 DevTools 中
    on(bookEvents.loadBooks, () => ({
      books: mockBooks,
    }))
  ),
  withHooks({
    onInit() {
      injectDispatch(bookEvents).loadBooks();
    },
  })
);

clearUndoRedo – 替代 store.clearStack

  • clearStack弃用
  • 请改用全新的独立函数 clearUndoRedo

clearUndoRedo 默认执行 软重置不会将状态设为 null)。可以通过选项请求 硬重置

clearUndoRedo(store, { lastRecord: null });

Gregor Woiwode 实现。已回移植至 NgRx Toolkit v19.5.0 和 v20.7.0。

ngrx-toolkit-openapi-gen

我们收到了来自 Murat Sari 的一份绝佳圣诞礼物:一个能够生成以下内容的 OpenAPI 生成器:

  • ✅ NgRx Signal Store
  • ✅ 资源(Resources)
  • ✅ 变更(Mutations)
  • ✅ 基于 Zod 架构的代码

生成的代码真的非常漂亮——在代码生成器中很少见到如此优雅的实现。

  • npm: (link)
  • Documentation: (link)

Future Release Strategy for Compatible Angular Versions

Toolkit 的第 21 版因新功能和修复而耗时更长。NgRx v21 与后续的 Toolkit v20 发行版兼容,但仅在某些覆盖的情况下。为提升体验,我们发布了 NgRx Toolkit v20.6.0,支持 NgRx v20 和 v21。同样适用于 v20.7.0,它向后移植了若干 v21 功能。

今后:
如果在任何即将到来的主要 Toolkit 发行版中出现障碍,我们将在下一个稳定的主要 NgRx 发行版准备好集成时,发布一个兼容的 次要 Toolkit 版本

感谢!

我们感谢所有为此贡献时间、专业知识、讨论和代码的人。特别感谢本文中突出显示的贡献者:

你们的努力让 NgRx Toolkit 社区更加强大。 🙏

Back to Blog

相关文章

阅读更多 »