多个RXJS Observable之间的共享数据



我正在重写一些代码,以使用RXJS和角度异步管道。代码非常简单,我们将在数组中添加和删除项。有没有比在BehaviorSubject中使用tap-set-store状态更好的方法来完成对共享数组的操作?

this.listSubject = new BehaviorSubject(['a','b','c']);
this.addSubject = new Subject<string>();
this.addAction$ = this.addSubject.asObservable().pipe(
withLatestForm(this.listSubject),
map(([itemToAdd, list]) => {list.push(itemToAdd); return list;}),
tap((data) => this.listSubject.next(data))
);
this.removeSubject = new Subject<number>();
this.removeAction$ = this.removeSubject.asObservable().pipe(
withLatestForm(this.listSubject),
map(([indexToRemove, list]) => {list.splice(indexToRemove, 1); return list;}),
tap((data) => this.listSubject.next(data))
);
this.list$ = merge(this.addAction$, this.removeAction$);

编辑:UI代码使用异步管道,

list$ | async 

类似这样的功能:

export class HelloComponent {
actionSubject = new Subject<Action>();
action$ = this.actionSubject.asObservable();
originalList$ = of(["a", "b", "c"]);
list$ = merge(this.originalList$, this.action$).pipe(
scan((acc: string[], action: any) => {
if (action.isDelete) {
return acc.filter(item => item !== action.item);
} else {
return [...acc, action.item];
}
})
);
onAdd() {
this.actionSubject.next({ item: "z", isDelete: false });
}
onRemove() {
this.actionSubject.next({ item: "b", isDelete: true });
}
}
export interface Action {
item: string;
// This could instead be an Enum of operations
isDelete: Boolean;
}

注意:我在这里对您的代码进行了堆叠:https://stackblitz.com/edit/angular-array-processing-deborahk

我创建了一个单独的动作流,它发出了一个动作对象,其中包含要添加/删除的项以及该动作是否为删除。(您可以将其更改为用于添加和删除的枚举(

然后,我使用scan来随着时间的推移保留项目集,并简单地添加到项目列表中或从中删除。

您的代码在app.component.ts文件中

修订后的代码在hello.component.ts文件中。

这适用于你的场景吗?

注意:对于那些说你需要订阅的人。。。如果使用异步管道,则不会。我的用户界面是这样的:

<div>{{ list$ | async}}</div>

一般来说,tap的大多数用法都与RxJS强硬派有关。

我最担心的是,你真的应该用订阅而不是点击来修改你的应用程序状态(行为主体(。您当前未显示list$的任何订阅者。如果没有人订阅,那么您的tap将永远不会运行。另一方面,如果list$最终有两个订阅者,那么您的点击将为每个事件运行两次!你可以在某个地方用publish来缓解这种情况,但我认为如果你只是。。。

this.listSubject = new BehaviorSubject(['a','b','c']);
this.addSubject = new Subject<string>();
this.addSubject.subscribe(itemToAdd => {
const currentValue = this.listSubject.value;
currentValue.push(itemToAdd);
this.listSubject.next(currentValue);
});
this.removeSubject = new Subject<number>();
this.removeSubject.subscribe(indexToRemove => {
const currentValue = this.listSubject.value;
currentValue.splice(indexToRemove, 1);
this.listSubject.next(currentValue);
});

我担心的第二个(稍微少一点(问题是,你正在从多个主题重新发布你的列表。现在您有三个主题,每个主题都可以发出相同的列表。这意味着你有三个潜在的真理来源。您还违反了CQS(命令-查询分离(。当某个东西调用addItem时,它不需要获取新的列表值。相反,任何使用列表的内容都会得到更新,因为您的列表主题已经更新。

额外阅读:在你的应用程序中,你有状态(列表(和可以修改状态的事件。或者,更一般地说,可以修改状态的"操作"。在像ngrx这样的工具中,您将看到描述该模型的"存储"(您的状态(和"操作"(事件(等概念。行为主体是类似ngrx商店的轻量级替代品。随着你的应用程序开始变得越来越复杂,你可能会从阅读ngrx商店等工具中获益匪浅。

为什么不使用函数?

list$ = new BehaviorSubject(['a','b','c']);
add(val) {
this.list$.next([...this.list$.value, val]);
}
remove(val) {
this.list$.next(...this.list$.value.filter(v => v !== val));
}

您的代码不清楚,是什么订阅了所有这些可观察的内容。您需要订阅您的动作可观察性来触发任何功能。您正在更改对象。

你的代码看起来真的很好,我唯一能想到的就是不变性。这样,每次添加或删除项目时,你都会得到一个新的列表:

this.addAction$ = this.addSubject.asObservable().pipe(
withLatestForm(this.listSubject),
map(([itemToAdd, list]) => [...list, itemToAdd]),
tap((data) => this.listSubject.next(data))
);
this.removeAction$ = this.removeSubject.asObservable().pipe(
withLatestForm(this.listSubject),
map(([indexToRemove, list]) =>
[...list.slice(0, indexToRemove), ...list.slice(indexToRemove + 1)]),
tap((data) => this.listSubject.next(data))
);

最新更新