【问题标题】:How to get previous value Subject?如何获得以前的价值主题?
【发布时间】:2021-02-19 09:55:21
【问题描述】:

我有以下主题:

  private active = new Subject<P>();
  prev: Custom<any>;

  set(p: P): void {
    this.active.next(p);
  }

我想从active 主题中提取数据并将其设置为prev,如下所示:

 set(p: P): void {
     this.prev = this.active;
     this.active.next(p);
 }

怎么做?

【问题讨论】:

    标签: angular rxjs behaviorsubject


    【解决方案1】:

    你最好在这里使用BehaviorSubject。它保存当前值,也有value getter 来获取它。

    private active = new BehaviorSubject<P>(null);  // <-- default value required
    prev: Custom<any>;
    
    set(p: P): void {
      this.prev = this.active.value;
      this.active.next(p);
    }
    

    但这实际上看起来并不那么干净。您打算如何使用prev 值?或许有一种更简洁的方式使用 ReplaySubject 和缓冲区 2。

    更新:避免同步.value getter

    我可以想出两种方法来使用流中的前一个值。

    1. pairwise 运算符的直接方式。它将最后一个值和当前值作为数组发出。不利的一面是它不会发出第一个值,因为还没有当前的先前状态。可以使用startWith 运算符进行调整。
    import { Subject } from "rxjs";
    import { startWith, pairwise} from "rxjs/operators";
    
    export class AppComponent {
      subSource = new Subject<any>();
      sub$ = this.subSource.pipe(
        startWith(null),
        pairwise()
      );
    
      constructor() {
        this.sub$.subscribe(val => console.log("pairwise:", val));
    
        this.subSource.next(1);
        this.subSource.next(2);
        this.subSource.next(3);
      }
    }
    
    // expected output:
    // pairwise: [null, 1]
    // pairwise: [1, 2]
    // pairwise: [2, 3]
    
    1. 如果您不希望在开头使用默认值,您可以将ReplaySubject 与缓冲区 2 一起使用,并使用 scan 运算符仅发出以前的值和当前值。
    import { ReplaySubject } from "rxjs";
    import { scan } from "rxjs/operators";
    
    export class AppComponent {
      replaySubSource = new ReplaySubject<any>(2);
      replaySub$ = this.replaySubSource.pipe(
        scan((acc, curr) => {
          acc.push(curr);
          return acc.slice(-2);
        }, [])
      );
    
      constructor() {
        this.replaySub$.subscribe(val => console.log("replay subject:", val));
    
        this.replaySubSource.next(1);
        this.replaySubSource.next(2);
        this.replaySubSource.next(3);
      }
    }
    
    // replay subject: [1]
    // replay subject: [1, 2]
    // replay subject: [2, 3]
    

    工作示例:Stackblitz

    关于使用find的默认值的问题。不,我不认为在那里使用find 很脏。事实上,find 是用来根据谓词获取特定元素的,所以我看不出你的用法有什么错误。

    更新:发射为{prev: .., active: ..}

    您只需将map 运算符插入到末尾并根据要求发出。

    sub$ = this.subSource.pipe(
      startWith(null),
      pairwise(),
      map(([prev, active]) => ({ prev: prev, active: active }))
    );
    
    // expected output:
    // pairwise: {prev: null, active: 1}
    // pairwise: {prev: 1, active: 2}
    // pairwise: {prev: 2, active: 3}
    

    我还调整了Stackblitz 以反映更改。

    【讨论】:

    • 但这实际上看起来并不那么干净。 - 你能评论一下,有什么问题吗?我只想存储上一个状态并在下次激活它
    • 这违背了 observable 的异步特性,因为我们在使用同步的 value getter “作弊”。事实上,有些人厌恶value getter 并积极建议避免使用它并认为它是反模式的。但如果它对你有用,那么它对你有用。
    • 好吧,假设我们有两个 observable:active:Observable 和 prev:Observable。每次我设置active 时,我都应该存储上一个值。然后将上一个值恢复为活动状态。不使用同步方法怎么办?
    • 如何从外部列表中将默认值设置为private active = new BehaviorSubject&lt;P&gt;(null); ?我可以像private active = new BehaviorSubject&lt;P&gt;(list.find((item) =&gt; item.default); 那样使用它吗?它看起来很丑
    • @AlexandraBalmush:是的,它可能使用了 RxJS fromEvent 函数。但这完全是另一个问题。我想说这个问题已经解决了。请尝试将fromEventswitchMap 之类的映射运算符一起使用。如果遇到问题,可以再写一个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-06
    • 1970-01-01
    • 1970-01-01
    • 2021-08-19
    • 2021-11-18
    • 2018-04-11
    • 1970-01-01
    相关资源
    最近更新 更多