Search Lessons, Code Snippets, and Videos
search by algolia
X
#native_cta# #native_desc# Sponsored by #native_company#

How to Avoid Observable Flicker on Initialzation

written by Jeff Delaney
full courses for pro members

Have you ever noticed that Firebase auth flickers for a split second when refreshing a page? This is an annoying little problem that happens because Firebase must validate the user’s auth state when the library is re-initilized. This is a common problem for any Observable that requires an initialization value from an external API. Fortunately, we can use the RxJS startWith operator - along with with the browser’s localStorage - to solve this problem.

Flickering auth in Firebase on page refresh

Simple Use Case

Let’s imagine we’re fetching an Observable from Angular’s HTTP client. There might be a second or two of latency on this request, so let’s provide a default value:

import { startWith } from 'rxjs/operators';

//...

const default = 'Hello';

const data = http.get('someAPI').pipe(
startWith(default)
)

Then stream will always have a value to unwrap, for example in the HTML:

{{ data | async }}
<!-- will show 'Hello' or the value from the API -->

Fixing AngularFire Firebase Auth Flicker

In episode 55 we built an Angular Firebase authentication system, but it suffers from this page refresh flicker issue. Let’s make some adjustments to fix it.

In the auth service, we will save the user data to Local Storage whenever the state changes using the tap operator, then use startWith to look for an initial value. In other words, if the user is already logged in and they refresh the page it will already have the user details available.

It only takes two additional lines of code to make happen:

import { switchMap, startWith, tap } from 'rxjs/operators';

// ... omitted

this.user = this.afAuth.authState.pipe(
switchMap(user => {
if (user) {
return this.afs.doc<User>(`users/${user.uid}`).valueChanges();
} else {
return of(null);
}
}),
// Add these lines to set/read the user data to local storage
tap(user => localStorage.setItem('user', JSON.stringify(user))),
startWith(JSON.parse(localStorage.getItem('user')))
);

Now our app will start with the user directly in Local Storage instead of waiting for the Firestore data to load over the network.