React Context配合Firebase监听用户文档时login内currentUser返回null问题
问题原因
- 你在login的then回调中直接读取
currentUser状态时拿到旧值,是因为React状态更新是异步的:signInWithEmailAndPassword执行成功后,onAuthStateChanged监听器会触发setCurrentUser更新状态,但这个状态更新不会立刻同步到当前函数闭包中,所以你读取到的还是执行login函数时的初始null值。 - 你当前把取消订阅函数存在state里的写法是可行的,但有逻辑遗漏:如果用户是刷新页面后保持的登录态,你没有触发用户文档的snapshot监听,另外注销时如果取消订阅函数为null会抛出调用错误。
修复方案
把用户文档的监听逻辑移到onAuthStateChanged的回调中,既能统一处理登录/刷新登录两种场景,也能直接拿到回调返回的最新用户对象,不需要依赖组件状态。
修正后的代码
import React, { useContext, useState, useEffect } from 'react' import { auth, db } from '../init/firebase' const AuthContext = React.createContext() export function useAuth() { return useContext(AuthContext) } export function AuthProvider({ children }) { const [currentUser, setCurrentUser] = useState(null) const [loading, setLoading] = useState(true) // User firebase doc init const [userDoc, setUserDoc] = useState({ loaded: false, data: [], }) const [firebaseUnsubscriber, setFirebaseUnsubscriber] = useState(null); function signup(email, password) { return auth.createUserWithEmailAndPassword(email, password) } function login(email, password) { return auth.signInWithEmailAndPassword(email, password) } function logout() { return auth.signOut().then(() => { // 调用前先判断是否存在取消订阅函数 if (firebaseUnsubscriber) { firebaseUnsubscriber(); setFirebaseUnsubscriber(null); } // 清空用户文档状态 setUserDoc({ loaded: false, data: [] }) }) } useEffect(() => { const unsubscriber = auth.onAuthStateChanged((user) => { setCurrentUser(user) // 先清理上一个用户的监听 if (firebaseUnsubscriber) { firebaseUnsubscriber(); setFirebaseUnsubscriber(null); } if (user) { // 直接用回调返回的user对象,不需要依赖state const unsub = db.collection("users").doc(user.uid).onSnapshot((doc) => { setUserDoc({ loaded: true, data: doc.data(), }) }); setFirebaseUnsubscriber(unsub); } setLoading(false) }) return () => { unsubscriber() // 组件卸载时清理用户文档监听 if (firebaseUnsubscriber) { firebaseUnsubscriber(); } } }, [firebaseUnsubscriber]) const value = { currentUser, userDoc, login, signup, logout, } return ( <AuthContext.Provider value={value}> {!loading && children} </AuthContext.Provider> ) }
内容的提问来源于stack exchange,提问作者No Sarcasm
相关产品推荐
相关产品推荐

