Ngrx:订阅时未在主题中收到当前值

Vin*_*els 5 rxjs behaviorsubject redux ngrx angular

在一个组件中,我使用 store 为我的状态的某些部分订阅了一个 Subject。从那时起,当那部分状态发生变化时,我将开始接收更新,我的组件将反映此状态。到现在为止还挺好。

但是,我还希望我的组件根据此状态的当前值正确初始化。因此,一旦我订阅,我还希望发出当前值 - 例如,我的视图可以正确初始化。

使用 Angular 组件的示例:

my.component.ts

export class MyComponent implements OnInit {
  constructor(private store: Store<State>) { }

  // As soon as I subscribe, I want this to also emit the current
  // value, e.g. so my view can correctly reflect the current state.
  public mode$ = new Subject<ApplicationMode>();

  ngOnInit() {
    this.store.select(getApplicationState)
       .select(s => s.applicationMode).subscribe(this.mode$);
  }
}
Run Code Online (Sandbox Code Playgroud)

我的组件.html

{{ mode$ | async }}
Run Code Online (Sandbox Code Playgroud)

我相信我想要/期望的是我商店的这部分会返回 aBehaviorSubject或 a ReplaySubject(1)。但是从商店中选择任何东西总是会返回一个Store<T>,这显然是某种主题,但在订阅时似乎没有发出当前值?

我猜,可能有效的是在应用程序初始化时订阅这一段状态,然后将该值传递给它的所有子组件,一直传递到这个组件,所以这个组件变成了一个愚蠢的组件而不是选择从商店本身。这可能是要走的路吗?或者我缺少一些基本的东西来完成这项工作吗?

byg*_*ace 4

Ngrx 确实像一个BehaviorSubject,当你第一次订阅时你会得到当前值。

mode$问题可能是您在订阅该主题之前将值从行为主题(存储)推送到普通主题 ( )。这意味着您将初始值推送到您的主题 ( mode$) 中,并且没有订阅者需要通知,因此它会失败。当订阅者订阅时,mode$它正在订阅普通主题,因此它不会获得当前值,但会获得所有未来值。

我建议的是去掉中间人,并将从商店返回的可观察值直接暴露给视图,如下所示:

this.mode$ = this.store.select(...);
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您将公开一个可观察量,该可观察量将提供订阅任何订阅者的当前值。

我发现很难找到有关异步管道在组件生命周期中订阅的位置的具体文档。因此,我整理了一个测试,在每个生命周期挂钩中发布到主题并通过异步管道订阅它。

代码:

import { 
  Component, 
  OnChanges, 
  OnInit, 
  DoCheck, 
  AfterContentInit, 
  AfterContentChecked, 
  AfterViewInit, 
  AfterViewChecked, 
  OnDestroy,
} from '@angular/core';

import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/do';

@Component({
  selector: 'app-test',
  template: '{{ data$ | async }}'
})
export class TestComponent implements 
  OnChanges, 
  OnInit, 
  DoCheck, 
  AfterContentInit, 
  AfterContentChecked, 
  AfterViewInit, 
  AfterViewChecked, 
  OnDestroy {
    public get data$() {
      return this.data.asObservable().do(x => console.log('event: ', x));
    }
    public data: Subject<string> = new Subject<string>();


  ngOnChanges() {
    this.data.next('ngOnChanges');
  }

  ngOnInit() {
    this.data.next('ngOnInit');
  }

  ngDoCheck() {
    this.data.next('ngDoCheck');
  }

  ngAfterContentInit() {
    this.data.next('ngAfterContentInit');
  } 

  ngAfterContentChecked() {
    this.data.next('ngAfterContentChecked');
  }

  ngAfterViewInit() {
    this.data.next('ngAfterViewInit');
  }

  ngAfterViewChecked() {
    this.data.next('ngAfterViewChecked');
  }

  ngOnDestroy() {
    this.data.next('ngOnDestroy');
  }
}
Run Code Online (Sandbox Code Playgroud)

结果:

event:  ngAfterViewInit
event:  ngAfterViewChecked
event:  ngDoCheck
event:  ngAfterContentChecked
event:  ngAfterViewChecked
Run Code Online (Sandbox Code Playgroud)

结果表明异步管道首先在 ngOnInit 之后执行,这是您首次订阅的位置。这可以解释为什么你错过了它。

  • @VincentSels np,我自己学习了:)我不知道异步管道何时评估。 (2认同)