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

时间:2017-11-15 14:34:19

标签: angular redux rxjs ngrx behaviorsubject

在一个组件中,我使用商店订阅了我所在州的某个部分的主题。从那时起,当状态的这一部分发生变化时,我将开始接收更新,我的组件将反映这种状态。到目前为止一切都很好。

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

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$);
  }
}

my.component.html

{{ mode$ | async }}

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

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

1 个答案:

答案 0 :(得分:4)

Ngrx的行为类似于BehaviorSubject,您可以在第一次订阅时获得当前值。

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

我建议切断中间人并将从商店返回的观察者直接暴露给视图:

this.mode$ = this.store.select(...);

通过这种方式,您将公开一个observable,它将在订阅任何订阅者时提供当前值。

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

代码:

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');
  }
}

结果:

event:  ngAfterViewInit
event:  ngAfterViewChecked
event:  ngDoCheck
event:  ngAfterContentChecked
event:  ngAfterViewChecked

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