简体   繁体   中英

How to remove the new firebase onAuthStateChanged listener in react

I'm implementing firebase auth in a react web app with react-router.

A user signs in (at /signin) with either Facebook or Google using the popup sign in, then if successful I route to the main app (/). In the main app component I listen for an auth state change:

  componentWillMount() {
    this.authListener = this.authListener.bind(this);
    this.authListener();
  }

authListener listens for the auth change:

authListener() {
    firebase.auth().onAuthStateChanged((user) => {
      if (user) {
        console.log('user changed..', user);
        this.setState({
          User: {
            displayName: user.displayName
          }
        });
      } else {
        // No user is signed in.
        browserHistory.push('/signin');
      }
    });
  }

Everything works fine, except when I sign out (and go back to /signin) and sign in again using facebook or google. Then I get an error saying:

Warning: setState(...): Can only update a mounted or mounting component.

I suspect that the onAuthStateChanged listener from the now unmounted previous logged in state app is still running.

Is there a way to remove the onAuthStateChanged listener when the App component unmounts?

Any listeners that you have set up will also need to be torn down.

Your suspicions are very much on-spot.

You should use the componentWillUnmount lifecycle method to remove any leftover listeners that might pollute your app.

To clear up the listener, here's the relevant code:

Inside your authListener function you need to save a reference to the listener inside your component (it is returned to you as a result of calling firebase.auth().onAuthStateChanged ). It will be a hook that will un-reference the listener and remove it.

So instead of just calling it, save the returned value as such

this.fireBaseListener = firebase.auth().onAuthStateChanged ...

And when your component un-mounts, use this code:

componentWillUnmount() {
   this.fireBaseListener && this.fireBaseListener();
   this.authListener = undefined;
}

I know I am late to the game, but here's a hooks based solution:

React.useEffect(() => {
    const unsubscribe = firebase.auth().onAuthStateChanged((user) => { // detaching the listener
        if (user) {
            // ...your code to handle authenticated users. 
        } else {
            // No user is signed in...code to handle unauthenticated users. 
        }
    });
    return () => unsubscribe(); // unsubscribing from the listener when the component is unmounting. 
}, []);

@Justin because onAuthStateChanged returns function so you can use it to clear the listener... this.fireBaseListener = firebase.auth().onAuthStateChanged

docs: https://firebase.google.com/docs/reference/js/firebase.auth.Auth#onAuthStateChanged

Returns non-null firebase.Promise containing non-null Array of string

Instead of checking onAuthStateChanged() function inside componentDidMount() you can check subscription's like this.

componentWillMount() {
    //following line will help you to setState() but not required
    let set =  this  

    //this is our trick
    this.unsubscribe  = firebase.auth().onAuthStateChanged(user => {
        if (!user) {
            //navigate to guest stack
            //actually im using react-navigation
            set.props.navigation.navigate('Guest'); 
        } else {
            //set current user to state 
            set.setState({
                user: user,
                loading: false
            })
        }
    });
}

//What you have to do next is unsubscribe ;) 
componentWillUnmount() {
    this.unsubscribe();
}

If you are using Firebase 9 you should pay attention to the way you call the function onAuthStateChanged

First way

import firebaseApp from 'path/to/your/firebase/app';
// you will need to do 
const auth = getAuth(firebaseApp);

useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged(user => {
        if (user) {
            // user is signed in
        } else {
            // user is signed out
        }
    });
    
    // unsubscribing from the listener when the component is unmounting.
    return unsubscribe;
}, [])

Second way

import firebaseApp from 'path/to/your/firebase/app';
import { getAuth, onAuthStateChanged } from 'firebase/auth';
const auth = getAuth(firebaseApp);


useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
        ...
    });
    
    // unsubscribing from the listener when the component is unmounting.
    return unsubscribe;
}, [])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM