Detach a Firestore listener inside an onAuthStateChanged listener

Viewed 209

I think I understand how to detach a listener by returning it, but in this specific case I'm not sure of the best way to do it.

I have a React component where I subscribe to auth changes with onAuthStateChanged.

If it returns an user : I create a listener to the data of this user in Firestore.

useEffect(() => {
    return firebase.auth.onAuthStateChanged(user => {
      if (user) {
        const unsubscribeFirestore = firebase.db
          .collection('users')
          .doc(user.uid)
          .onSnapshot(doc => {
            setUser({ user, ...doc.data() })
          })
      } else {
        setUser({ user })
      }
    })
  }, [firebase])

Where and how should I return unsubscribeFirestore to detach it ?

2 Answers

It seems your code already is implicitly returning the unsubscribe callback from firebase.auth.onAuthStateChanged, but you can make it more imperative by capturing the returned unsubscribe function in a locally scoped variable.

You can capture the inner snapshot unsubscribe in a React ref to be accessed in the useEffect cleanup function.

const snapShotUnsubscribeRef = useRef();

useEffect(() => {
  const unsubscribe = firebase.auth.onAuthStateChanged(user => {
    if (user) {
      snapShotUnsubscribeRef.current = firebase.db
        .collection('users')
        .doc(user.uid)
        .onSnapshot(doc => {
          setUser({ user, ...doc.data() })
        })
    } else {
      setUser({ user })
    }
  });

  return () => {
    snapShotUnsubscribeRef.current?.();
    // Or if you can't use Optional Chaining:
    //   snapShotUnsubscribeRef.current && snapShotUnsubscribeRef.current()
    unsubscribe();
  };
}, [firebase]);

Here is the link to the official Firebase docs for more details.

The answer Drew gave is correct. To add I don't think you need to worry about the nested second listener since the if clause is checking if there is a user or not and if there is none then the second listener does not apply anyway. You can try both use cases to test if it is working or not.

Here is a link dealing with this use case. So this should work.

Related