Change in the authState stream behavior in 4.0.0-rc0 vs previous versions when doing a .map of that authState stream. Sorry I don't have Plunker ready for this but the repo code using the Angular CLI below should be easy to follow.
Angular:
"@angular/core": "^4.0.0",
Firebase:
"firebase": "3.9.0",
AngularFire:
"angularfire2": "4.0.0-rc.0",
Other (e.g. Ionic/Cordova, Node, browser, operating system):
Mac 10.12.4, Node v7.5.0, Chrome 57.0.2987.133
I was doing a .map of the authState stream to make a boolean stream for signed in status. Something I might use for an auth.gaurd.ts
Failing code demonstrating the problem
app.component.ts
import { Component } from '@angular/core';
import { AngularFireAuth } from "angularfire2/auth";
import * as firebase from 'firebase/auth';
import { Observable } from "rxjs/Observable";
import 'rxjs/add/operator/map';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private afAuth: AngularFireAuth) {
afAuth.authState.subscribe((user: firebase.User) => {
if (user && user.uid) {
console.log("User is signed in. uid: ", user.uid);
} else {
console.log("User is not signed in :(");
}
});
}
get isSignedInStream(): Observable<boolean> {
return this.afAuth.authState.map<firebase.User, boolean>((user: firebase.User) => {
console.log("Is signed in stream", user);
return user != null;
});
}
}
app.component.html
Testing for an infinite auth loop
<pre>{{isSignedInStream | async | json}}</pre>
Comment out the line above as your first test to make the .map stream not have a subscriber and therefore never run. Uncomment the line above activate the isSignedInStream and cause an infinite loop.
Steps to set up and reproduce
ng new AuthInfiniteLoop
cd AuthInfiniteLoop/
npm install angularfire2 firebase --save
(follow the setup steps within environments and app.module.ts. app.module.ts shown below as a reference, environments not shown but it's what you'd guess)
ng serve
Follow the advice in the html file.
app.module.ts
(just to see if I messed anything up)
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
import { AngularFireModule } from 'angularfire2';
import { environment } from '../environments/environment';
import { AngularFireAuthModule } from 'angularfire2/auth';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
AngularFireModule.initializeApp(environment.firebaseConfig), // imports firebase/app needed for everything
AngularFireAuthModule, // imports firebase/auth, only needed for auth features
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
User is signed in. uid: fisherds
U {ba: Array(0), j: "AIzaSyBUZoLZPumsnN_AJLRPAqOswhPT80nmTuo", D: "ME435 Web Remote", v: "fisherds-me435.firebaseapp.com", g: S鈥
auth.service.ts:25 Is signed in stream U {ba: Array(0), j: "AIzaSyBUZoLZPumsnN_AJLRPAqOswhPT80nmTuo", D: "ME435 Web Remote", v: "fisherds-me435.firebaseapp.com", g: S鈥
auth.service.ts:25 Is signed in stream U {ba: Array(0), j: "AIzaSyBUZoLZPumsnN_AJLRPAqOswhPT80nmTuo", D: "ME435 Web Remote", v: "fisherds-me435.firebaseapp.com", g: S鈥
auth.service.ts:25 Is signed in stream U {ba: Array(0), j: "AIzaSyBUZoLZPumsnN_AJLRPAqOswhPT80nmTuo", D: "ME435 Web Remote", v: "fisherds-me435.firebaseapp.com", g: S鈥
... (repeats forever causing chrome tab to go non responsive)
User is not signed in :(
Is signed in stream null
Is signed in stream null
Is signed in stream null
... (repeats forever causing chrome tab to go non responsive)
Expected one call to my .map stream. As has worked in AngularFire2 pre 4.0.0-rc0 for many months. I have used this approach with my auth.gaurd.ts many times in the past
Infinite loop where my .map is being called repeatedly forever.
@fisherds You are creating an observable inside of a getter. Change detection will call this getter over and over again which will lead to your infinite loop. Can you try to remove the getter and create the isSignedIn observable in the constructor or ngOnInit?
constructor(afAuth: AngularFireAuth) {
this.isSignedIn = afAuth.authState.map<firebase.User, boolean>((user: firebase.User) => {
console.log("Is signed in stream", user);
return user != null;
});
}
Thanks Dave, yeah that totally fixed my issue. In short I've been doing it wrong for awhile, but I was getting away with it before. Thanks for your help!
Thank you!!! This was puzzling me...
Most helpful comment
@fisherds You are creating an observable inside of a getter. Change detection will call this getter over and over again which will lead to your infinite loop. Can you try to remove the getter and create the
isSignedInobservable in theconstructororngOnInit?