Search code examples
react-nativereact-native-firebase

How to overcome that the useEffect runs multiple times using React Native Firebase package?


Im trying to build a simple android app using React Native Firebase for authentication listening to onAuthStateChanged. If I understand correctly the onAuthStateChanged method have multiple states so it will trigger useEffect multiple times. The issue is that I want to trigger a function after authentication that checking if the user is included in a firebase-database, but due to this that function will get triggered multiple times and the will return undefined (twice) when I call it within the onAuthStateChanged function.

    const [userDetails, setUserDetails] = useState();
    const [initializing, setInitializing] = useState(true);

     function onAuthStateChanged(user) {
        if (user !== null) {
          setUserDetails({
            name: user.displayName,
            email: user.email,
            photo: user.photoURL,
            theme: 'default',
          });
          if (initializing) setInitializing(false); 
          checkUserInDB()
        } else {
          setUserDetails(null);
          setInitializing(true);
        }
      }

  useEffect(() => {
    const subscriber = auth().onAuthStateChanged(onAuthStateChanged);
    return subscriber; // unsubscribe on unmount
  }, []);

To overcome this I can call checkUserInDb() function by leave it outside of onAuthStateChanged waiting for userDetails and initializing states to be fulfilled:

if (userDetails !== null && initializing === false) {
        checkUserInDb()

Although this will return the userDetails, if I log it it happens 3 times. I was trying to tackle it from a few angles (await /then/ timeout) but the issue is in useEffect and i just ran out of ideas. If you could point me to the right direction it would be appreciated.


Solution

  • I've spent a few hours figuring out the issue and I successfully overcome it. I believe the onAuthStateChanged didn't like that I tried to set a custom state with the userDetails above. By using just user like in the documentation, I didn't have undefined returned. But it still not the solution to the problem as it will indeed onAuthStateChanged run 2-3 times before its settle. I found an other thread where someone used debouncing which I implemented and with a bit of refactoring my original logic it started working.

      var debounceTimeout;
      const DebounceDueTime = 200; // 200ms
    
      function onAuthStateChanged(user) {
        if (debounceTimeout) clearTimeout(debounceTimeout);
        debounceTimeout = setTimeout(() => {
          debounceTimeout = null;
          handleAuthStateChanged(user);
        }, DebounceDueTime);
      }
    
      function handleAuthStateChanged(user) {
        setUser(user);
        if (initializing) setInitializing(false);
        if (user !== null) {
          console.log('=========> User is authenticated already:', user);
          checkUserInDb(user);
        } else {
          console.log('=========> User not found or signed out:', user);
        }
      }
    
    useEffect(() => {
        const subscriber = auth().onAuthStateChanged(onAuthStateChanged);
        return subscriber; // unsubscribe on unmount
      }, []);