Firebase auth().onAuthStateChanged未等待signInWithCredential完成如何解决
问题核心原因
onAuthStateChanged 是Firebase Auth SDK的内置状态监听,只要用户认证凭证校验通过(即signInWithCredential执行成功)就会立刻触发,和后续Firestore的读写操作完全独立,无法直接延迟它的触发时机,你遇到的是典型的Auth层和数据层操作的竞态问题。
解决方案
方案1(推荐):登录流程全完成后再跳转首页
从根源避免首页提前加载触发监听,把登录页的异步回调嵌套改为async/await写法,确保老用户校验、新用户数据写入全部执行完成后,再执行路由跳转进入首页。
修改后的登录页代码:
const _signIn = async () => { setInitializing(true); try { await GoogleSignin.hasPlayServices(); const userInfo = await GoogleSignin.signIn(); const credential = auth.GoogleAuthProvider.credential( userInfo.idToken, userInfo.accessToken, ); // 完成认证 const response = await auth().signInWithCredential(credential); const uid = response.user.uid; const usersRef = firestore().collection('users'); const firestoreDocument = await usersRef.doc(uid).get(); // 新用户写入数据 if (!firestoreDocument.exists) { const data = { uid: uid, email: userInfo.user.email, fullname: userInfo.user.name, bio: 'Halo!! ..', username: uid.substring(0, 8), }; await usersRef.doc(data.uid).set(data); } // 所有操作完成再跳转首页,此时首页监听触发时数据已存在 setInitializing(false); navigation.navigate('Home'); // 替换为实际的首页路由名 } catch (error) { setInitializing(false); if (error.code === statusCodes.SIGN_IN_CANCELLED) { Alert.alert('Sign in canceled'); } else if (error.code === statusCodes.IN_PROGRESS) { Alert.alert('Signin in progress'); } else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) { Alert.alert('PLAY_SERVICES_NOT_AVAILABLE'); } else { Alert.alert(error.message); } } };
方案2:优化首页监听逻辑,增加容错等待
如果业务逻辑需要首页提前加载监听认证状态,可以修改首页的监听逻辑,不直接读取不存在的文档数据,改为监听文档快照,等文档写入完成后再执行后续操作。
修改后的首页代码:
useEffect(() => { let authUnsubscribe = null; const init = async () => { try { const netState = await NetInfo.fetch(); if(!netState.isConnected){ Alert.alert('No Internet Connection Detected'); setInitializing(false); return; } setInitializing(true); // 监听认证状态 authUnsubscribe = auth().onAuthStateChanged((user) => { if (user) { const usersRef = firestore().collection('users'); // 监听文档快照,等待文档存在后再执行后续逻辑 const docUnsubscribe = usersRef.doc(user.uid).onSnapshot((document) => { if (document.exists) { const userData = document.data().uid; setisLogin(userData); setInitializing(false); docUnsubscribe(); // 取消文档监听避免内存泄漏 } }, (error) => { setInitializing(false); Alert.alert(error.message); docUnsubscribe(); }); } else { setInitializing(false); } }); } catch (error) { setInitializing(false); Alert.alert(error.message); } }; init(); // 组件卸载时取消监听避免内存泄漏 return () => { if (authUnsubscribe) authUnsubscribe(); } }, []);
注意事项
- 原首页代码中给
onAuthStateChanged加await是无效的,该方法返回的是取消监听的函数,不是Promise,无需加await - 所有异步监听方法要在组件卸载时调用取消函数,避免内存泄漏
内容的提问来源于stack exchange,提问作者Mukhlis Saputro
相关产品推荐
相关产品推荐

