RxJS shareReplay()不会发出更新的值 [英] RxJS shareReplay() does not emit updated value

查看:140
本文介绍了RxJS shareReplay()不会发出更新的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用户服务,该服务允许登录,注销并维护有关当前登录用户的数据:

I have a user service which allows login, logout and maintains data about the currently logged in user:

user$ = this.http.get<User>('/api/user')
            .pipe(
              shareReplay(1),
            );

我正在使用 shareReplay(1),因为我不想多次调用该Web服务.

I am using shareReplay(1) because I do not want the webservice to be called several times.

为简单起见,在其中一个组件上,我有这个功能,但是如果用户登录,我还有其他几件事要做:

On one of the components, I have this (for simplicity), but I have several other things I want to do if a user is logged in:

<div *ngIf="isUserLoggedIn$ | async">Logout</div>

isUserLoggedIn$ = this.userService.user$
                    .pipe(
                      map(user => user != null),
                      catchError(error => of(false)),
                    );

但是, isLoggedIn $ 在用户登录或注销后不会更改.刷新页面时确实会更改.

However, the isLoggedIn$ does not change after the user logs in or logs out. It does change when I refresh the page.

这是我的注销代码:

logout() {
  console.log('logging out');
  this.cookieService.deleteAll('/');

  this.user$ = null;

  console.log('redirecting');
  this.router.navigateByUrl('/login');
}

我知道,如果我将变量分配为null,则内部可观察对象不会重置.

I understand that the internal observable is not reset if I assign the variable to null.

因此,对于注销,我从以下答案中得到了提示: https://stackoverflow.com/a/56031703 关于刷新 shareReplay().但是,模板中使用的user $会使我的应用程序在尝试注销时立即陷入困境.

So, for logout, I took clue from this answer: https://stackoverflow.com/a/56031703 about refreshing a shareReplay(). But, the user$ being used in the templates causes my application to go into a tizzy as soon as I attempt to logout.

在我的尝试之一中,我尝试了 BehaviorSubject :

In one of my attempts, I tried BehaviorSubject:

user$ = new BehaviorSubject<User>(null);

constructor() {
  this.http.get<User>('/api/user')
    .pipe(take(1), map((user) => this.user$.next(user))
    .subscribe();
}

logout() {
  ...
  this.user$.next(null);
  ...
}

除了刷新页面外,此方法效果更好.auth-guard( CanActivate )始终将用户$作为null并重定向到登录页面.

This works a little better except when I refresh the page. The auth-guard (CanActivate) always gets the user$ as null and redirects to the login page.

当我刚开始的时候,这似乎是一件容易的事,但是每次更改我都会陷入一个更深的洞里.有解决办法吗?

This seemed like an easy thing to do when I started out, but I am going on falling into a deeper hole with each change. Is there a solution to this?

推荐答案

对于这种情况,我将数据流(用户)与操作流(登录用户)一起使用,并使用 combineLatest 进行操作合并两个:

For scenarios like this, I use a data stream (user) with an action stream (log user in) and use combineLatest to merge the two:

  private isUserLoggedInSubject = new BehaviorSubject<boolean>(false);
  isUserLoggedIn$ = this.isUserLoggedInSubject.asObservable();

  userData$ = this.http.get<User>(this.userUrl).pipe(
    shareReplay(1),
    catchError(err => throwError("Error occurred"))
  );

  user$ = combineLatest([this.userData$, this.isUserLoggedIn$]).pipe(
    map(([user, isLoggedIn]) => {
      if (isLoggedIn) {
        console.log('logged in user', JSON.stringify(user));
        return user;
      } else {
        console.log('user logged out');
        return null;
      }
    })
  );

  constructor(private http: HttpClient) {}

  login(): void {
    this.isUserLoggedInSubject.next(true);
  }

  logout(): void {
    this.isUserLoggedInSubject.next(false);
  }

我在这里有一个有效的stackblitz: https://stackblitz.com/edit/angular-user-logout-deborahk

I have a working stackblitz here: https://stackblitz.com/edit/angular-user-logout-deborahk

user $ 是用户数据流和发出布尔值的 isUserLoggedIn $ 流的组合.这样,我们可以使用映射并将返回的值映射到用户,如果用户已注销,则将其映射为null.

The user$ is the combination of the user data stream and the isUserLoggedIn$ stream that emits a boolean value. That way we can use a map and map the returned value to the user OR to a null if the user has logged out.

希望类似的东西对您有用.

Hope something similar works for you.

这篇关于RxJS shareReplay()不会发出更新的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆