I recently changed my DB method to get a user as a live observable instead of a promise. Prior to that I had a separate action to re-load the user if a field was changed through the UI. The live observable works really well as changes to the DB are instantly reflected in the app. Unfortunately this broke my logout button and I don't understand why nor how to fix it.
Auth service that triggers DB loading of user
this.afAuth.authState.subscribe((user) => {
if (user) {
const appUser: AuthUser = {
uid: user.uid,
name: user.displayName,
photoURL: user.photoURL,
};
this.store.dispatch(userActions.loggedIn({ user: appUser }));
} else {
this.store.dispatch(userActions.logout());
}
});
Relevant actions
loggedIn: createAction('[User] Logged in', props<{ user: AuthUser }>()),
userLoaded: createAction('[User] User loaded', props<{ dbUser: DbUser }>()),
logout: createAction('[User] Logout'),
Action responsible to update state
getDbUser = createEffect(() =>
this.actions$.pipe(
ofType(userActions.loggedIn),
switchMap(({ user: { uid } }) =>
this.databaseService.getUser(uid).pipe( // <- this only correctly fires once
map((dbUser) => userActions.userLoaded({ dbUser })), // <- this fires after the logout action and I don't know why
// first() <- this solves the logout issue but breaks the live updates
)
)
)
);
Get a live connection with AngularFire
getUser(uid: AppUser['uid']): Observable<DbUser> {
return this.afs
.collection<DbUser>(constants.dbCollections.users)
.doc(uid)
.snapshotChanges()
.pipe(map((action) => action.payload.data()));
}
Reducer
// Login
on(userActions.loggedIn, (state, payload) => ({
...initialState,
user: payload.user,
})),
on(userActions.userLoaded, (state, payload) => ({
...state,
user: {
...payload.dbUser,
roles: new Set(payload.dbUser?.roles),
licenses: new Set(payload.dbUser?.licenses),
},
})),
on(userActions.logout, (state) => ({ ...initialState })),
Why does userActions.userLoaded({ dbUser })
keep firing? No user data is modified in the DB upon logout.
How do I stop it from firing after the logout action?
CodePudding user response:
Without more code, it's difficult to understand it correctly.
You probabably need to unsubscribe
to this.actions$
after logout and subscribe in the login
CodePudding user response:
I found one way to fix it.
getUser(uid: AppUser['uid']): Observable<DbUser> {
return this.afs
.collection<DbUser>(constants.dbCollections.users)
.doc(uid)
.snapshotChanges()
.pipe(
map((action) => action.payload.data()),
takeUntil(this.afAuth.authState.pipe(first((x) => !x))) // Fix
);
}
When I add a dependency on AngularFireAuth
in my database service I can use takeUntil
in the getUser method. I'm not sure how I feel about the dependency though, if there is a cleaner more decoupled solution that would still be welcome.