【问题标题】:BehaviorSubject: mutating current value between subscriptionsBehaviorSubject:在订阅之间改变当前值
【发布时间】:2021-12-25 00:41:09
【问题描述】:

假设我有带有用户界面的 BehaviorSubject:

interface User {
  firstName: string;
  lastName: string;
}

let user: User = {
  firstName: 'Cuong',
  lastName: 'Le',
};

let bs = new BehaviorSubject<User>(user);

有两个订阅,sub1 试图更改名字。 Sub2 稍后订阅,并且用户对象的名字也更改了,因为 sub1 之前确实更改了它:

let sub1 = bs.subscribe((u) => {
  u.firstName = 'An';

  console.log(u);
});

let sub2 = bs.subscribe((u) => {
  console.log(u);
});

当这种情况发生在大型 Angular 应用程序中时,很难调试。订阅时我们如何使值不可变?

我正在寻找深度不可变的解决方案,以防止其他地方的代码更改数据而不是影子

【问题讨论】:

标签: javascript angular typescript rxjs


【解决方案1】:

使用这个answer 并将您的类型包装在这个不可变实用程序类型中应该可以解决您的问题。与 readonly 关键字或 ReadOnly 实用程序类型不同,此答案中的 Immutable 接口将递归地将所有属性设为只读。

interface User {
  firstName: string;
  lastName: string;
  address: { 
    street: string;
  } 
}

let user: User = {
  firstName: 'Cuong',
  lastName: 'Le',
  address: { 
    street: '123 Sesame St.'
  }
};

let bs = new BehaviorSubject<Immutable<User>>(user);

let sub1 = bs.subscribe((u) => {
  u.address.street = '456'; //<-- Cannot assign to 'street' because it is a read-only property.
  console.log(u);
});

【讨论】:

  • 感谢您的回答,它如何与属性级别 2 一起使用?
  • 得到了一个适用于嵌套属性的答案。
【解决方案2】:

您可以使用实用程序类型 Readonly&lt;T&gt; 将 BehaviorSubject 上的类型标记为 Readonly&lt;User&gt; 而不是 User

let bs = new BehaviorSubject<Readonly<User>>(user);

现在,当您尝试修改时,TS 会警告您:

无法分配给“firstName”,因为它是只读属性。 (2540)

【讨论】:

  • 2 级属性如何工作?
  • 好点,这在分配嵌套属性时不会发出警告。您可能会发现 this SO question 和这个 github issue 感兴趣。他们提到了一些实现DeepReadonly&lt;T&gt;的方法。
【解决方案3】:

这是一个我不得不多次解决的问题。只要您通过主题传递简单的非圆形对象,此解决方案就应该有效。

一个要求是您使用lodash 库,它支持非常快速的深度复制(无参考副本)。我到处寻找,但我还没有找到更快的。

let bs = (new BehaviorSubject<User>(user)).pipe(    
     map((userData) => _.cloneDeep(userData)) 
);

我已经在相当大的状态对象上使用过它,我一直对它执行复制的速度印象深刻。这里的含义是,任何来自主题的发射都是原始的副本,因此变化是孤立发生的,永远无法追溯到源头。

【讨论】:

  • 有没有不使用lodash的解决方案?
  • 当然。而不是使用 cloneDeep,只需创建一个新对象并手动复制所有值。我愿意每天都使用 lodash,而不是手动操作。
猜你喜欢
  • 1970-01-01
  • 2021-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多