奇异变化检测行为Angular 2+

cup*_*_of 9 angular ngrx-store

您好我正在开发一个大型应用程序,我遇到了一些变更检测问题.我将尝试尽可能地解释我的设置.

父组件ts:

运用 changeDetection: ChangeDetectionStrategy.OnPush

我有一个可观察的变量

loaderOverlay$: Observable<boolean>;

this.loaderOverlay$ = this.store.pipe(
  select(selectors.loaderOverlaySelector)
);
Run Code Online (Sandbox Code Playgroud)

此变量从子组件的rxjs操作更新.然后通过rxjs进程.(动作 - >减速器 - >选择器)

父组件HTML

<div *ngif="(loaderOverlay$ | async)"></div>

孩子#1组件(我发送我的动作):

myFunction() {
  this.store.dispatch(new actions.LoaderOverlay(true));
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,一旦我发出动作,*ngif就会非常不稳定.它似乎没有按照我想要的方式工作(调度操作,将值更改为true以便显示div).这很奇怪,因为如果我console.log(action.payload)在reducer中,该值实际上正在更新,但*ngif它不起作用.甚至更奇怪的是,当我将鼠标悬停在其他组件上时,它似乎开始了.

我花了很多时间在这上面,我想我已经缩小了它以改变检测,因为在父组件中如果我这样做:

ngAfterViewChecked(){
   this.changeDetector.detectChanges();
}
Run Code Online (Sandbox Code Playgroud)

它似乎对我有用.我的问题是,ngAfterViewChecked似乎被触发了很多次,我害怕性能问题.

以前有没有人遇到过这个问题?或者有没有人对于发生了什么以及我可以做些什么来解决这个奇怪的问题?

谢谢!

And*_*tar 5

loaderOverlay$发出的值是否评估为true?我在 stackblitz 上创建了一个类似的代码(如您所描述的),但我似乎无法重现您遇到的错误。它要么是在 angular/ngrx 中修复的错误,要么是您发出的值未评估为 true(选择不好并返回 undefined 或其他内容)。尝试点击 observable 并记录结果。

https://stackblitz.com/edit/angular-ngrx-update?file=src/app/app.component.css

后来编辑:

正如 Cristian Traina 在评论中指出的那样,markForCheck(从异步管道内部)从外部 angular 调用时不会触发更改检测。作为修复,您可以在管道中使用区域调度程序,或者检测更改的来源并修补该区域以触发 angular 内的更改检测(因为您的应用程序的其他区域可能存在问题)。

https://www.npmjs.com/package/ngx-zone-scheduler?activeTab=readme

public constructor(
  @Inject(ZoneScheduler)
  private readonly zoneScheduler: SchedulerLike,
) {}

this.loaderOverlay$ = this.store.pipe(
  select(selectors.loaderOverlaySelector),
  observeOn(this.zoneScheduler),
);
Run Code Online (Sandbox Code Playgroud)

或者你可以tickappRef

public constructor(
  private appRef: ApplicationRef,
) {}

this.loaderOverlay$ = this.store.pipe(
  select(selectors.loaderOverlaySelector),
  tap(_ => this.appRef.tick()),
);
Run Code Online (Sandbox Code Playgroud)


小智 3

从您的示例来看,该策略似乎onPush没有正确使用。该onPush策略意味着如果组件@inputs没有发生变化,即使组件中的变量正在更新,组件也不一定会更新。从我在您的示例中收集到的信息来看,父组件的@input引用没有更改,这意味着更改检测没有按您的预期发生。

为了解决这个问题,我建议更新父组件(正在订阅商店更新)以根据需要使用defaultStrategy或手动调用markForCheck()来触发更新。