React-native-firebase: onAuthStateChanged invocation adds up on React Native reload (CMD+R) in iOS only in v6.0.1

Created on 10 Oct 2019  路  22Comments  路  Source: invertase/react-native-firebase


Issue



Background:
I am using auth().onAuthStateChanged to determine if user is logged in or not (as intended by this method). The listener is registered ONCE on App-Startup and lives until the App dies. I register onAuthStateChanged in a Redux-Saga, but for reproduction purpose I can show this behavior also with simple arrow function components.

The Issue (iOS only!):
During DEV if you reload Metro Bundler with CMD+R (iOS), there is no chance to unsubscribe the onAuthStateChanged listener, even if I try, because the app dies on reload before unsubscribing. After each reload the invocation adds up multiple times (because it is registered again) and is fired as often as a reload took place. I assume the listener still lives in native iOS in the background, although JS was completely restarted by Metro Bundler.

During development I experience very strange behavior in my App due to this issue. So I need to force the App to close in iOS and restart it, which is extremely inconvenient.

Maybe there is another approach to this use case, that I missed? How to unsubscribe onAuthStateChanged before reloading? Thank you in advance.

I did not experience this behavior on iOS in deprecated RNFirebase v5.5.5 but only in 6.0.1
Android seems to be fine in v6.0.1.


Project Files

import React, {useState} from 'react';
import {
  SafeAreaView,
  Text,
  Button
} from 'react-native';

import auth from '@react-native-firebase/auth';
let i = 0;
const unsubscribe = auth().onAuthStateChanged((user) => {
  console.log(`I was invoked ${i++} times`)
});

const App = () => {

  const [user] = useState(auth().currentUser)

  const login = () => {
    const email = "[email protected]"
    const password = "!Password123"
    auth()
      .signInWithEmailAndPassword(email, password)
      .catch(error => {
        throw error;
      });
  }

  const signout = () => {
    auth().signOut()
  }

  return (
      <SafeAreaView>
       <Text>{user && user.displayName ||聽'Please Login'}</Text>
       {!user && 
       <Button title={'Login'} onPress={() => login()}/>
       }
       {user && 
       <Button title={'Signout'} onPress={() => signout()}/>
       }
       <Text>Press CMD+R to reload, then perform Login/Signout action and see console how onAuthStateChanged invocation adds up. Repeat.</Text>
      </SafeAreaView>
  );
};
export default App;

Console Output after 10x reloads:

(index):181 Console was cleared
index.delta:35141 Running application Firebase601 ({
    initialProps =     {
    };
    rootTag = 101;
})
index.delta:35141 Running "Firebase601" with {"rootTag":101,"initialProps":{}}
index.delta:35141 I was invoked 0 times
index.delta:35141 I was invoked 1 times
index.delta:35141 I was invoked 2 times
index.delta:35141 I was invoked 3 times
index.delta:35141 I was invoked 4 times
index.delta:35141 I was invoked 5 times
index.delta:35141 I was invoked 6 times
index.delta:35141 I was invoked 7 times
index.delta:35141 I was invoked 8 times
index.delta:35141 I was invoked 9 times
index.delta:35141 I was invoked 10 times
index.delta:35141 I was invoked 11 times
index.delta:35141 I was invoked 0 times

iOS

Click To Expand

#### `ios/Podfile`: - [ ] I'm not using Pods - [x] I'm using Pods and my Podfile looks like:

# N/A
#### `AppDelegate.m`:
// N/A


Android

Click To Expand

#### Have you converted to AndroidX? - [ ] my application is an AndroidX application? - [ ] I am using `android/gradle.settings` `jetifier=true` for Android compatibility? - [ ] I am using the NPM package `jetifier` for react-native compatibility? #### `android/build.gradle`:

// N/A
#### `android/app/build.gradle`:
// N/A
#### `android/settings.gradle`:
// N/A
#### `MainApplication.java`:
// N/A
#### `AndroidManifest.xml`:
<!-- N/A -->


Environment

Click To Expand

**`react-native info` output:**

System:
    OS: macOS 10.15
    CPU: (4) x64 Intel(R) Core(TM) i7-6567U CPU @ 3.30GHz
    Memory: 420.40 MB / 16.00 GB
    Shell: 5.7.1 - /bin/zsh
  Binaries:
    Node: 10.16.0 - ~/.nvm/versions/node/v10.16.0/bin/node
    Yarn: 1.16.0 - ~/.yarn/bin/yarn
    npm: 6.10.1 - ~/.nvm/versions/node/v10.16.0/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  SDKs:
    iOS SDK:
      Platforms: iOS 13.0, DriverKit 19.0, macOS 10.15, tvOS 13.0, watchOS 6.0
  IDEs:
    Android Studio: 3.5 AI-191.8026.42.35.5791312
    Xcode: 11.0/11A420a - /usr/bin/xcodebuild
  npmPackages:
    react: 16.8.1 => 16.8.1 
    react-native: 0.61.2 => 0.61.2
- **Platform that you're experiencing the issue on**: - [X] iOS - [ ] Android - [ ] **iOS** but have not tested behavior on Android - [ ] **Android** but have not tested behavior on iOS - [ ] Both - **`react-native-firebase` version you're using that has this issue:** - `6.0.1` - **`Firebase` module(s) you're using that has the issue:** - `Auth` - **Are you using `TypeScript`?** - `No`




Think react-native-firebase is great? Please consider supporting all of the project maintainers and contributors by donating via our Open Collective where all contributors can submit expenses. [Learn More]

Bug Authentication >= 6

Most helpful comment

v6.0.2 is now published with this fix in - thanks

All 22 comments

This should be handled automatically on the native side. When a reload is triggered, this code should be triggered: https://github.com/invertase/react-native-firebase/blob/master/packages/auth/ios/RNFBAuth/RNFBAuthModule.m#L79

It may be worth adding in some logging here in your code, and rebuild to see what's going on?

@Ehesp thank you, while you were writing I was investigating that very exakt RNFBAuthModule.m#L79 and I was wondering why it does not invoke. You mean adding code in RNFBAuthModule.m? I'm not very into writing ObjectiveC... Happy for a suggestion, thank you so much.

PS: Could anyone confirm this behavior is reproducable?

Yeah you should be able to just add in something like:

NSLog(@"Logging here");

Then rebuild the iOS app. The log should appear in Xcode or the react-native log-ios command.

Very odd! It indeed fires, but still the Listener is invoked multiple times after reload in JS.

RNFBAuthModule.m#L79 ff

  for (NSString *key in authStateHandlers) {
    FIRApp *firebaseApp = [RCTConvert firAppFromString:key];

    [[FIRAuth authWithApp:firebaseApp] removeAuthStateDidChangeListener:[authStateHandlers valueForKey:key]];
    [authStateHandlers removeObjectForKey:key];
    NSLog(@"Listeners should have been invalidated...");
  }

Xcode console:

2019-10-10 12:02:50.438560+0200 Firebase601[17345:262922] Listeners should have been invalidated...

What the...?

Can you log in the JS where it creates the listener and how many times it triggers? Console log should do

Modified my initial code like this:

App.js:

import auth from '@react-native-firebase/auth';
let i = 0;
console.log('I am created here')
const unsubscribe = auth().onAuthStateChanged((user) => {
  console.log(`I was invoked ${i++} times`)
});

Console Log:

Console was cleared
setUpDeveloperTools.js:73 I am created here
setUpDeveloperTools.js:73 I was invoked 0 times
setUpDeveloperTools.js:73 I was invoked 1 times
setUpDeveloperTools.js:73 I was invoked 2 times
setUpDeveloperTools.js:73 I was invoked 3 times
setUpDeveloperTools.js:73 I was invoked 4 times
setUpDeveloperTools.js:73 I was invoked 5 times
setUpDeveloperTools.js:73 I was invoked 6 times
setUpDeveloperTools.js:73 I was invoked 7 times

I very much do appreciate your help, @Ehesp 馃憤

Could it be in JS onAuthStateChanged somehow "survives" the reload? Very unlikely I guess.
I also ruled out the RN 0.61.2 "Fast Refresh" causes this, I turned it off.

Edit: OR could it be the authStateHandlers ~array~ dictionary in RNFBAuthModule.m does not contain all keys?

If I add the following, it will only return one Key, namely __FIRAPP_DEFAULT:

  for (NSString *key in authStateHandlers) {
    FIRApp *firebaseApp = [RCTConvert firAppFromString:key];

    [[FIRAuth authWithApp:firebaseApp] removeAuthStateDidChangeListener:[authStateHandlers valueForKey:key]];
    [authStateHandlers removeObjectForKey:key];
    NSLog(@"Listener Key: %@", key);
    NSLog(@"Listeners should have been invalidated...");
  }

  for (NSString *key in authStateHandlers) {
    NSLog(@"Listener if key was still there you could read it here: %@", key);
    // Does not show = evidence the dictionary is empty
  }

Edit: it seems https://github.com/invertase/react-native-firebase/blob/master/packages/auth/ios/RNFBAuth/RNFBAuthModule.m#L99 only adds this this single key. I'm very puzzled...

RCT_EXPORT_METHOD(addAuthStateListener:
  (FIRApp *) firebaseApp
) {
  if (![authStateHandlers valueForKey:firebaseApp.name]) {
    FIRAuthStateDidChangeListenerHandle newListenerHandle =
        [[FIRAuth authWithApp:firebaseApp] addAuthStateDidChangeListener:^(
            FIRAuth *_Nonnull auth,
            FIRUser *_Nullable user
        ) {
          NSLog(@"Listener fires in Objective-C");
          if (user != nil) {
            [RNFBSharedUtils sendJSEventForApp:firebaseApp name:AUTH_STATE_CHANGED_EVENT body:@{
                keyUser: [self firebaseUserToDict:user]
            }];
          } else {
            [RNFBSharedUtils sendJSEventForApp:firebaseApp name:AUTH_STATE_CHANGED_EVENT body:@{}];
          }
        }];
    authStateHandlers[firebaseApp.name] = [NSValue valueWithNonretainedObject:newListenerHandle];
    NSLog(@"Listener Key Assignment: %@", firebaseApp.name);
  }
}

Xcode Log:

2019-10-10 12:37:54.476236+0200 Firebase601[18008:293684] Listener Key: __FIRAPP_DEFAULT
2019-10-10 12:37:54.476484+0200 Firebase601[18008:293684] Listeners should have been invalidated...
**// Reload is finished here**
2019-10-10 12:37:55.137873+0200 Firebase601[18008:293842] Listener Key Assignment: __FIRAPP_DEFAULT
2019-10-10 12:37:55.137995+0200 Firebase601[18008:293118] Listener fires in Objective-C
2019-10-10 12:37:57.943161+0200 Firebase601[18008:293118] Listener fires in Objective-C
2019-10-10 12:37:57.943680+0200 Firebase601[18008:293118] Listener fires in Objective-C
2019-10-10 12:37:57.944010+0200 Firebase601[18008:293118] Listener fires in Objective-C
2019-10-10 12:37:57.944303+0200 Firebase601[18008:293118] Listener fires in Objective-C
2019-10-10 12:37:57.944577+0200 Firebase601[18008:293118] Listener fires in Objective-C

We only create a single event per app instance on native, and fan the events out in JS land. How come your JS code is invoking 7 subscribers? Where do you have that code in React context?

@Ehesp, the complete code was in my initial post, I did not add anything else.
The subscribers fire as often as I reload the App. If I reload it 10 times, it will fire 10 times from now on.

Steps to reproduce:

  1. react-native init newproject
  2. yarn add @react-native-firebase/app @react-native-firebase/auth
  3. Setup Firebase in iOS according to your docs here
  4. Replace App.js with this code (please read further below)
import React from 'react';
import {
  SafeAreaView,
  Text,
  Button
} from 'react-native';

import auth from '@react-native-firebase/auth';
let i = 0;
const unsubscribe = auth().onAuthStateChanged((user) => {
  console.log(`I was invoked ${i++} times`)
});

const App = () => {

  const user = auth().currentUser

  const login = () => {
    const email = "[email protected]"
    const password = "!Password123"
    auth()
      .signInWithEmailAndPassword(email, password)
      .catch(error => {
        throw error;
      });
  }

  const signout = () => {
    auth().signOut()
  }

  return (
      <SafeAreaView>
       <Text>{user && user.displayName ||聽'Please Login'}</Text>
       {!user && 
       <Button title={'Login'} onPress={() => login()}/>
       }
       {user && 
       <Button title={'Signout'} onPress={() => signout()}/>
       }
       <Text>Press CMD+R to reload, then perform Login/Signout action and see console how onAuthStateChanged invocation adds up. Repeat.</Text>
      </SafeAreaView>
  );
};
export default App;
  1. Launch App with react-native run-ios (In my case npx react-native run-ios --simulator "iPhone 11 Pro")
  2. Tap the "Login" Button, THEN reload for lets say 7 times in a row.
  3. Afterwards tap "Signout" and see how the console is flushed with "I was invoked X times"

I still suspect the Objective-C side to not unsubscribe. I was trying to trace it down to the original Firebase Libraries behind, but my Objective-C knowledge is fairly bad. :-(

I can even reduce this to this lines of code, which makes the effect visible without UI interaction.
Just reload the App with CMD+R and have a log at the console.

Edit: Suspecting iOS as the root cause, can we investigate iOS NotificationCenter somehow, to see how many subscribers are in memory?

// App.js
import React from 'react';
import auth from '@react-native-firebase/auth';
let i = 0;

console.log('I am created here')
const unsubscribe = auth().onAuthStateChanged((user) => {
  console.log(`I was invoked ${i++} times`)
});

const email = "[email protected]"
const password = "!Password123"
auth()
  .signInWithEmailAndPassword(email, password)
  .catch(error => {
    throw error;
  });
auth().signOut()

const App = () => {
  return null
};
export default App;

Can anybody reproduce this issue? Thank you in advance. Desperately...

I'm sorry I haven't reproduced this - and I don't have time :-( - but I will say your current investigation and your reduction of the problem to such a small snippet is fantastic work - I can't imagine any other path to take as to digging

Would you be able to add some logging into our JS code? I'd be interested to see if this is infact us or a RN environment thing.

@Ehesp yes sure, please see below. I also added more Obj C logging. TLDR: Your code seems to be ok. Android is ok as well. So I suspect the Firebase POD and/or iOS Notification Center. My Objective-C knowledge has ultimately come to an end here 馃槩 Any ideas highly appreciated 馃檶

Table of Contents:

  • Section 1: My App Code with logging
  • Section 2: Your @react-native-firebase/auth JS code with logging

    - Section 3: RN console Logs of 1 + 2

  • Section 4: Your RNFBAuthModule ObjC POD with logging

  • Section 5: Firebase FIRAuth ObjC POD with logging

    - Section 6: Xcode console logs of 4 + 5

Section 1: My App Code with logging

// App.js
import React from 'react';
import auth from '@react-native-firebase/auth';
let i = 0;

console.log('1锔忊儯 RN MyApp: Subscriber is created here ONCE')
const unsubscribe = auth().onAuthStateChanged((user) => {
  console.log(`馃毃 RN MyApp: Subscriber was invoked ${i++} times, user is ${user ? 'signed in' : 'signed out'}.`)
});

const email = "[email protected]"
const password = "!Password123"

console.log('2锔忊儯 RN MyApp: User signs IN now')
auth()
.signInWithEmailAndPassword(email, password)
.catch(error => {
  throw error;
});

console.log('3锔忊儯 RN MyApp: User signs OUT now')
auth().signOut()

console.log('馃槻 RN MyApp: Promises/Subscribers go wild from here')

const App = () => {
  return null
};
export default App;

Section 2: Your @react-native-firebase/auth JS code with logging

// index.js of @react-native-firebase/auth
...
    this.emitter.addListener(this.eventNameForApp('auth_state_changed'), event => {
    console.log('馃敟 RN Firebase: Event auth_state_changed is invoked')
      this._setUser(event.user);
      this.emitter.emit(this.eventNameForApp('onAuthStateChanged'), this._user);
    });
...
  onAuthStateChanged(listener) {
    console.log('馃敟 RN Firebase: Subscriber is created here ONCE')
    const subscription = this.emitter.addListener(
      this.eventNameForApp('onAuthStateChanged'),
      listener,
    );

    if (this._authResult) {
      listener(this._user || null);
    }
    return () => subscription.remove();
  }
...

Section 3: RN console Logs of 1 + 2

RN Console after 3 (!) Reloads

Console was cleared
setUpDeveloperTools.js:73 1锔忊儯 RN MyApp: Subscriber is created here ONCE
setUpDeveloperTools.js:73 馃敟 RN Firebase: Subscriber is created here ONCE
setUpDeveloperTools.js:73 馃毃 RN MyApp: Subscriber was invoked 0 times, user is signed in.
setUpDeveloperTools.js:73 2锔忊儯 RN MyApp: User signs IN now
setUpDeveloperTools.js:73 3锔忊儯 RN MyApp: User signs OUT now
setUpDeveloperTools.js:73 馃槻 RN MyApp: Promises/Subscribers go wild from here
setUpDeveloperTools.js:73 馃敟 RN Firebase: Event auth_state_changed is invoked
setUpDeveloperTools.js:73 馃毃 RN MyApp: Subscriber was invoked 1 times, user is signed in.
setUpDeveloperTools.js:73 馃敟 RN Firebase: Event auth_state_changed is invoked
setUpDeveloperTools.js:73 馃毃 RN MyApp: Subscriber was invoked 2 times, user is signed out.
setUpDeveloperTools.js:73 馃敟 RN Firebase: Event auth_state_changed is invoked
setUpDeveloperTools.js:73 馃毃 RN MyApp: Subscriber was invoked 3 times, user is signed out.
setUpDeveloperTools.js:73 馃敟 RN Firebase: Event auth_state_changed is invoked
setUpDeveloperTools.js:73 馃毃 RN MyApp: Subscriber was invoked 4 times, user is signed out.
setUpDeveloperTools.js:73 馃敟 RN Firebase: Event auth_state_changed is invoked
setUpDeveloperTools.js:73 馃毃 RN MyApp: Subscriber was invoked 5 times, user is signed in.
setUpDeveloperTools.js:73 馃敟 RN Firebase: Event auth_state_changed is invoked
setUpDeveloperTools.js:73 馃毃 RN MyApp: Subscriber was invoked 6 times, user is signed in.
setUpDeveloperTools.js:73 馃敟 RN Firebase: Event auth_state_changed is invoked
setUpDeveloperTools.js:73 馃毃 RN MyApp: Subscriber was invoked 7 times, user is signed in.

Section 4: Your RNFBAuthModule ObjC POD with logging

...
- (void)invalidate {
  for (NSString *key in authStateHandlers) {
    FIRApp *firebaseApp = [RCTConvert firAppFromString:key];

    [[FIRAuth authWithApp:firebaseApp] removeAuthStateDidChangeListener:[authStateHandlers valueForKey:key]];
    [authStateHandlers removeObjectForKey:key];
    NSLog(@"馃敟 RN Firebase: Listener Key: %@", key);
    NSLog(@"馃敟 RN Firebase: Listeners should have been invalidated...");
  }

  for (NSString *key in authStateHandlers) {
    NSLog(@"馃敟 RN Firebase: If listener key was still there you could read it here (QED): %@", key);
  }

}
...
RCT_EXPORT_METHOD(addAuthStateListener:
  (FIRApp *) firebaseApp
) {
  if (![authStateHandlers valueForKey:firebaseApp.name]) {
    FIRAuthStateDidChangeListenerHandle newListenerHandle =
        [[FIRAuth authWithApp:firebaseApp] addAuthStateDidChangeListener:^(
            FIRAuth *_Nonnull auth,
            FIRUser *_Nullable user
        ) {
          NSLog(@"馃敟 RN Firebase: Listener fires in Objective-C");
          if (user != nil) {
            [RNFBSharedUtils sendJSEventForApp:firebaseApp name:AUTH_STATE_CHANGED_EVENT body:@{
                keyUser: [self firebaseUserToDict:user]
            }];
          } else {
            [RNFBSharedUtils sendJSEventForApp:firebaseApp name:AUTH_STATE_CHANGED_EVENT body:@{}];
          }
        }];
    authStateHandlers[firebaseApp.name] = [NSValue valueWithNonretainedObject:newListenerHandle];
    NSLog(@"馃敟 RN Firebase: Listener Key Assignment: %@", firebaseApp.name);
  }
}
...

Section 5: Firebase FIRAuth ObjC POD with logging

...
- (FIRAuthStateDidChangeListenerHandle)addAuthStateDidChangeListener:
    (FIRAuthStateDidChangeListenerBlock)listener {
    NSLog(@"鈽笍 Firebase Auth Pod: Listener Handle adding: %@", listener);
  __block BOOL firstInvocation = YES;
  __block NSString *previousUserID;
  return [self addIDTokenDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) {
    BOOL shouldCallListener = firstInvocation ||
         !(previousUserID == user.uid || [previousUserID isEqualToString:user.uid]);
    firstInvocation = NO;
    previousUserID = [user.uid copy];
    if (shouldCallListener) {
      listener(auth, user);
    }
  }];
}

- (void)removeAuthStateDidChangeListener:(FIRAuthStateDidChangeListenerHandle)listenerHandle {
    NSLog(@"鈽笍 Firebase Auth Pod: Listener Handle removing: %@", listenerHandle);
  [self removeIDTokenDidChangeListener:listenerHandle];
}
...

Section 6: Xcode console logs of 4 + 5

// First run
2019-10-11 19:50:56.214861+0200 Firebase601[1989:82700] 鈽笍 Firebase Auth Pod: Listener Handle adding: <__NSStackBlock__: 0x700000e41240>
2019-10-11 19:50:56.215149+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C // Initial call
2019-10-11 19:50:56.215169+0200 Firebase601[1989:82700] 馃敟 RN Firebase: Listener Key Assignment: __FIRAPP_DEFAULT
2019-10-11 19:50:56.223666+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C // Sign in
2019-10-11 19:50:57.002923+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C // Sign out
// Okay until here!

// Reload
2019-10-11 19:51:46.934264+0200 Firebase601[1989:83169] 鈽笍 Firebase Auth Pod: Listener Handle removing: {length = 8, bytes = 0xa0ef1e0100600000}
2019-10-11 19:51:46.934457+0200 Firebase601[1989:83169] 馃敟 RN Firebase: Listener Key: __FIRAPP_DEFAULT
2019-10-11 19:51:46.934583+0200 Firebase601[1989:83169] 馃敟 RN Firebase: Listeners should have been invalidated...
2019-10-11 19:51:47.703943+0200 Firebase601[1989:83172] 鈽笍 Firebase Auth Pod: Listener Handle adding: <__NSStackBlock__: 0x700000f47240>
2019-10-11 19:51:47.704334+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:47.704604+0200 Firebase601[1989:83172] 馃敟 RN Firebase: Listener Key Assignment: __FIRAPP_DEFAULT
2019-10-11 19:51:47.711141+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:47.711410+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:48.553133+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:48.554134+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C

// Reload
2019-10-11 19:51:51.477565+0200 Firebase601[1989:83170] 鈽笍 Firebase Auth Pod: Listener Handle removing: {length = 8, bytes = 0x806b1d0100600000}
2019-10-11 19:51:51.478152+0200 Firebase601[1989:83170] 馃敟 RN Firebase: Listener Key: __FIRAPP_DEFAULT
2019-10-11 19:51:51.478366+0200 Firebase601[1989:83170] 馃敟 RN Firebase: Listeners should have been invalidated...
2019-10-11 19:51:52.026941+0200 Firebase601[1989:82699] 鈽笍 Firebase Auth Pod: Listener Handle adding: <__NSStackBlock__: 0x700000dbe240>
2019-10-11 19:51:52.027252+0200 Firebase601[1989:82699] 馃敟 RN Firebase: Listener Key Assignment: __FIRAPP_DEFAULT
2019-10-11 19:51:52.027255+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:52.033008+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:52.033587+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:52.034200+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:52.887787+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:52.888621+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:52.889324+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C

// Reload
2019-10-11 19:51:54.305055+0200 Firebase601[1989:83170] 鈽笍 Firebase Auth Pod: Listener Handle removing: {length = 8, bytes = 0x000c1d0100600000}
2019-10-11 19:51:54.305265+0200 Firebase601[1989:83170] 馃敟 RN Firebase: Listener Key: __FIRAPP_DEFAULT
2019-10-11 19:51:54.305368+0200 Firebase601[1989:83170] 馃敟 RN Firebase: Listeners should have been invalidated...
2019-10-11 19:51:55.024877+0200 Firebase601[1989:82692] 鈽笍 Firebase Auth Pod: Listener Handle adding: <__NSStackBlock__: 0x700000d3b240>
2019-10-11 19:51:55.025187+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:55.025186+0200 Firebase601[1989:82692] 馃敟 RN Firebase: Listener Key Assignment: __FIRAPP_DEFAULT
2019-10-11 19:51:55.034910+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:55.035799+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:55.036284+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:55.036743+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:55.833852+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:55.834726+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:55.835754+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C
2019-10-11 19:51:55.836747+0200 Firebase601[1989:82113] 馃敟 RN Firebase: Listener fires in Objective-C

we are having the same issue. We register onAuthStateChanged on app start and it causes CMD+R command to crash the application. We use react-native 0.61.2

Is anybody out there able to reproduce this issue with this steps?

Okay so here's what's going on currently:

  1. onAuthStateChanged is called. Like the Web SDK, this triggers an immediate callback.
  2. A sign in event is called, which triggers a callback.

So, these two steps are functioning correctly. However, when the app is reloaded (CMD + R), the context of the app is not destroyed, so calling the above steps creates a brand new event subscriber. As you keep reloading the app, these events keep stacking.

When you call sign out, a new event is then triggered which is calling all the active subscribers which is why the logs go crazy. So this is actually JS causing multiple subscribers.

In Prod, this won't happen as there is no way to hot reload.

cc @Salakar is there anyway we can flush listeners on reload in JS land?

@Ehesp Thank you for clarification 馃檹 I was about going mad with this issue.
I know in production this won't happen, this is totally well. It's only during development where this could be very annoying 馃槈

Ok pushed up a "fix" as a PR, hopefully. When the app was being reloaded the iOS events weren't correctly being removed/cleaned up.

v6.0.2 is now published with this fix in - thanks

You guys are awesome 馃

unsubscribe is a function that will be called to end the subscription, or return a function that call the unsubscribe() method

import React, { useState, useEffect } from 'react';
import { SafeAreaView, Text, Button } from 'react-native';

import auth from '@react-native-firebase/auth';
// let i = 0;
// const unsubscribe = auth().onAuthStateChanged((user) => {
//   console.log(`I was invoked ${i++} times`);
// });

const App = () => {
  const [user] = useState(auth().currentUser);

  const login = () => {
    const email = '[email protected]';
    const password = '!Password123';
    auth()
      .signInWithEmailAndPassword(email, password)
      .catch((error) => {
        throw error;
      });
  };

  const signout = () => {
    auth().signOut();
  };

    // .TSX code example

    //   useEffect(() => {
    //     let i = 0;

    //     // Observable *
    //     // ref: https://firebase.google.com/docs/auth/web/manage-users?hl=es#get_the_currently_signed-in_user
    //     const unsubscribe: firebase.Unsubscribe = firebase
    //       .auth()
    //       .onAuthStateChanged(
    //         (user: firebase.User | null) => {
    //           console.log(
    //             `Invoked: [${i++}] times,`,
    //             `uid: ${user?.uid},`,
    //             `email: ${user?.email},`,
    //             `isAnonymous: ${user?.isAnonymous}.`
    //           );
    //         },
    //         (error: firebase.auth.Error) => {
    //           console.error(error);
    //         }
    //       );

    //     return () => {
    //       console.log('onAuthStateChanged:UNMOUNTED');

    //       unsubscribe();
    //     };
    //   }, []);

  useEffect(() => {

    let i = 0;
    const unsubscribe = auth().onAuthStateChanged((user) => {
      console.log(`I was invoked ${i++} times`);
    });

    // unsubscribe is a function that will be called to end the subscription 
    return unsubscribe;

  }, []);

  return (
    <SafeAreaView>
      <Text>{(user && user.displayName) || 'Please Login'}</Text>
      {!user && <Button title={'Login'} onPress={() => login()} />}
      {user && <Button title={'Signout'} onPress={() => signout()} />}
      <Text>
        Press CMD+R to reload, then perform Login/Signout action and see console
        how onAuthStateChanged invocation adds up. Repeat.
      </Text>
    </SafeAreaView>
  );
};
export default App;

Was this page helpful?
0 / 5 - 0 ratings