How can I prevent Firebase from repeatedly triggering change detection in Angular 2?

695 views Asked by At

Firebase uses a lot of internal asynchronous calls that trigger change detection because of Angular/Zone monkey-patching websockets and setInterval etc. Even when I'm not interacting with my app, I see a cascade of change detection happening all the time, which contributes to slowing things down especially on mobile.

This default behavior could be useful, but the way I'm using Firebase right now I have pretty tight control over when I need to update the view, so callbacks from Firebase are used in such a way that change detection will happen manually anyway.

I know setting change detector strategy to OnPush will help here, which I'm working on, but I want to attack this from every angle.

I'm familiar with zone's runOutsideAngular but not sure now to apply it here since all of the async calls are happening inside the Firebase module.

How can I get Firebase to do all its business outside the angular zone?


Edit: Example code that shows the problem:

import {Component} from '@angular/core';

@Component({
  selector: 'test-component',
  template: 'hello world'
})
export class TestComponent {
  ref: Firebase;

  constructor() {
    this.ref = new Firebase('<firebase URL>');
  }

  ngDoCheck() {
    console.log('Check the stack - this is being called continually via async functions used internally by Firebase.');
    debugger;
  }
}
1

There are 1 answers

0
tobek On BEST ANSWER

This is obvious in retrospect, but simply instantiating the Firebase reference outside of the zone did the trick. E.g.:

import {Injectable, NgZone} from '@angular/core';

// ...

@Injectable()
export class DataService {
  ref: Firebase;

  // ...

  constructor(
    private zone: NgZone
    // ...
  ) {
    // Instantiate Firebase ref outside the zone to prevent its continual internal operations from triggering change detection
    this.zone.runOutsideAngular(() => {
      this.ref = new Firebase('<firebase URL>');
    });

    this.ref.on('value', snapshot => {
      // Change detection will NOT automatically be triggered after this callback
    });
  }
}

By putting breakpoints in ngDoCheck in my components I was able to trace all the change detection cycles back to that line where I instantiate the Firebase reference, which hinted that running it outside the zone would prevent them.

Note that if you go this route you have to make sure to change detection is getting triggered when necessary. See https://stackoverflow.com/a/34829089/458614 for examples.