首页 > 解决方案 > NGXS如何在异步调用后触发Angular重新渲染?

问题描述

我正在从我的商店进行一些 api 调用,并且我有一个捕获错误,当抛出错误时触发带有错误消息的模式。问题是,当发生这种情况时,会调用触发模式的方法,但在我单击页面上的某个位置之前不会呈现 html。这只发生在商店内,我已经在应用程序的几个部分进行了模拟,如下所示:

timer(5000)
      .pipe(
        mergeMap(() => {
          throw new Error('Some error');
        }),
      )
      .pipe(
        catchError((error) => {
          return this.handleError(_(`Couldn't do the thing`))(error);
        }),
      )
      .subscribe((result) => {
        console.log(result);
      });

我以为我可以注入ChangeDetectorRef来触发 html 的手动重新渲染,但我得到了NullInjectorError: No provider for ChangeDetectorRef!,但我无法让它工作。我的问题是:

是否可以在商店中注入,ChangeDetectorRef它会解决我的问题吗?另外,作为后续问题,是否有其他方法可以规避此问题?根据我一直在阅读的一些事情,这似乎是由于商店在 Angular 范围之外而发生的,所以它不知道需要重新渲染某些东西。

任何帮助将非常感激。

更新:是一个堆栈闪电战,通过调度一个操作来显示错误消息来说明问题和可能的解决方案。

标签: angularngxs

解决方案


通常更改检测由 zone.js 自动触发,更具体地说,是在 NgZone 中注册的每个(微)任务之后。

默认情况下,NGXS 不在 NgZone 中运行操作处理程序。这是一种有用的性能优化,在大多数情况下您不会注意到差异。特别是当动作处理程序只修改实际状态并且没有副作用时更是如此。但是在您的情况下,动作处理程序有副作用:this.handleError(_("Couldn't do the thing"))(error),或confimationService.triggerConfirmation()在 StackBlitz 中。这种副作用甚至反映在视图中。

现在,仍然有很多方法可以解决这个问题。您所需要的只是在副作用之后触发的单个更改检测周期。这就是它真正有趣的地方:虽然动作处理程序本身并不在 NgZone 中运行,但在 NgZone 中运行着很多周围的代码。这可能确实会触发上述变更检测周期。尤其:

  • 如果您的操作是同步的,并且调度它的代码在 NgZone 中运行,那么在当前(微)任务结束时触发的更改检测循环将在副作用之后运行。
  • 如果您订阅Observable返回的 from store.dispatch,那么该订阅将在 NgZone 内发出并完成。因此,这会在副作用之后触发两个变化检测周期。

(顺便说一句,后者是调度两个嵌套动作在您的 Stackblitz 中起作用的原因:您在那里订阅调度方法!)

如果您想自己调查事件的顺序,请查看此 Stackblitz。控制台输出应该非常准确地告诉您每个场景中发生了什么。

最后,让我们谈谈如何确保正确触发变更检测。实际上有几个选项可供选择:

  1. 虽然您不能ChangeDetectorRef在状态中注入 a,但可以注入ApplicationRef. 调用其tick方法将在当前(微)任务结束时异步触发更改检测周期。
  2. 如果你想利用 zone.js,你也可以注入NgZone并使用它的run方法在 NgZone 中运行你的整个副作用。这具有额外的优势,即由副作用注册的(微)任务也将跟随变化检测周期。
  3. 如果你想在 NgZone 中运行你的所有代码,你可以executionStrategy: NoopNgxsExecutionStrategy在你的NgxsConfig. 这将覆盖 NGXS 的默认行为并全局导致所有操作处理程序在 NgZone 中运行。

推荐阅读