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.
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
}, []);