angular - BehaviorSubject 和 concatMap 的副作用
问题描述
这是我第一次尝试 BehaviorSubject、异步管道和 concatMap,所以我在更新 DOM 中的数据时遇到了一些问题。
我有:
private profilesStore = new BehaviorSubject<any>(null);
profiles$ = this.profilesStore.asObservable();
getUserProfiles(): Observable<Profile[]> {
const headers = this.authService.getHeaders();
return this.http.get<any>(`${API_URL}/profile`, { headers: headers })
.pipe(
catchError(err => throwError(err)),
tap(res => this.profilesStore.next(res)),
shareReplay()
);
}
接着
addProduct(profileId: any) {
const headers = this.authService.getHeaders();
return this.http.post<any>(`${apiUrl}/products/${profileId}`, {}, { headers: headers })
.pipe(
catchError(err => throwError(err)),
concatMap(() => this.profileService.profiles$),
map(profiles => {
const selectedProfile = profiles.findIndex(profile => profile.id === profileId);
profiles[selectedProfile].canEdit = true;
return profiles;
})
);
}
这个逻辑就像购物车逻辑。我将产品添加到其中一个配置文件,因此为了避免再次调用 api (getUserProfiles),我修改了 profile$ 流并添加了我想要的属性(在本例中为 canEdit),但是当我从购物车中删除产品时出现问题并从 getUserProfiles() 恢复数据我知道,当我将 concatMap 与 profile$ 一起使用时,即使我没有调用该函数,我也会对 addProduct() 产生副作用,我的问题是......
为什么它继续执行
map(profiles => {
const selectedProfile = profiles.findIndex(profile => profile.id === profileId);
profiles[selectedProfile].canEdit = true;
return profiles;
})
使用我过去作为参数传递的旧 profileId,即使我没有调用 addProduct() 函数,如何避免这种情况?
解决方案
将 observables 想象成一个水龙头,一旦你订阅它,水龙头就永远不会关闭。只要有水,水就会一直流动。只有当您取消订阅它(或任何其他终止它的运营商)时,水龙头才会关闭。
所以当你做 a 的那一刻addProfile()
,哪怕只是一次,它的 observable 就永远打开了,只要有数据(水)的排放,数据的排放仍然会继续,即使你addProfile()
不再调用该函数 ->而且,如果您两次调用该函数,您实际上有两个订阅,也就是两个水管和水龙头;这是大多数开发人员没有注意的事情。
因此,当您addProfile()
第一次打电话时,由于您从未取消订阅,您实际上转而收听,profiles$
这要归功于
concatMap(() => this.profileService.profiles$),
,你的数据流实际上变成了监听那个 BehaviourSubject 的变化,这就解释了即使你不再调用addProfile()
,但是因为你更新了你的profiles$
,排放仍然通过,并且数据流会流过管道,然后,它将有效地执行下一个管道运算符:
map(profiles => {
const selectedProfile = profiles.findIndex(profile => profile.id === profileId);
profiles[selectedProfile].canEdit = true;
return profiles;
})
推荐阅读
- css - WebStorm CSS 无法解析自定义属性
- ios - 在 iOS 中显示全屏视频的视频分辨率
- amazon-ec2 - Angular Routing 在 URL 地址栏上不起作用
- javascript - 将确认消息的结果传递给 spring mvc 控制器
- amazon-web-services - 无法将 Vuejs 项目部署到 AWS ECS
- laravel-5 - certbot 证书在 laravel 5 应用程序上是否安全?
- ruby-on-rails - 使用 Rails API 的活动管理员
- opengl - 许多光源的阴影映射 - 它很慢吗?
- android - java.lang.IllegalArgumentException: View=DecorView@ff5fb5d[MainActivity] 未附加到窗口管理器
- java - 如何在 Spring Boot 安全性中使以前的用户会话无效