You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React useAuth结合Firestore路由首次加载读取次数异常求助

Firestore首次加载读取次数超标问题排查建议

问题描述

我在Firestore控制台发现,应用首次加载时的读取次数远超预期(预期1次,实际消耗9次),推测某些内容渲染次数超标。尝试在访问Firestore时加console.log,但没找到问题。作为React新手,查了很多帖子和文档还是搞不懂,求建议。

相关代码

index.js

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <StrictMode>
    <BrowserRouter>        
      <AuthProvider>
        <App />        
      </AuthProvider>
    </BrowserRouter>
  </StrictMode>
)

userContext.js

const AuthContext = createContext()
    
async function logIn(email, password) { 
  await signInWithEmailAndPassword(auth, email, password)
}

async function logOut() {
  await signOut(auth)  
}

async function register(email, password) {
  return createUserWithEmailAndPassword(auth, email, password)
}

export function AuthProvider({ children }) {    
  const [userData, setUserData] = useState(null)
    
  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, async (authUser) => {
      if (authUser) {
        try {             
          const docRef = doc(db, 'USERS', authUser.uid)
          const docSnap = await getDoc(docRef)
          const data = docSnap.data()    
          setUserData(data)          
        } catch (error) {
          console.error(error)
        }
      } else {
        setUserData(null)
      }
    })

    return () => unsubscribe()
  }, [])

  const authContextValue = {    
    userData,        
    logIn,
    logOut,
    register,
  }

  return (
    <AuthContext.Provider value={authContextValue}>
      {children}
    </AuthContext.Provider>
  )
}

export function useAuth() {
  const context = useContext(AuthContext)

  if (!context) {
    throw new Error('No context provider')
  }

  return context
}

App.js

export default function App() {   
  const { userData } = useAuth()

  return (
    <>    
      {userData ? (
      <Routes>
        <Route path="/" 
          element={
            <>
              <AppNavbar /> 
              <Outlet />
            </>
          }>
          
          <Route path="page1" element={<Page1/>} />
          <Route path="page2" element={<Page2/>} />
        </Route>
      </Routes>
      
      ) : (      
        <Routes>
          <Route path="login" element={<Login />} />
          <Route path="register" element={<Register />} />          
        </Routes>
      
      )}

  </>
  )
}

排查与解决建议

  • React StrictMode的双重执行:开发环境下,<StrictMode>会重复执行Effect和生命周期函数,包括你onAuthStateChanged里的getDoc请求,直接导致读取次数翻倍甚至多次触发。可以先注释掉<StrictMode>测试,生产环境StrictMode不会有这个问题,若要开发时避免就直接移除。

  • 避免重复获取用户数据:onAuthStateChanged在Auth状态初始化过程中可能多次回调,比如从null到authUser的过程。可以加判断逻辑,只有当用户数据未加载或用户ID不匹配时才请求Firestore:

    useEffect(() => {
      const unsubscribe = onAuthStateChanged(auth, async (authUser) => {
        if (authUser) {
          // 仅当userData为空或uid不匹配时才发起请求
          if (!userData || userData.uid !== authUser.uid) {
            try {             
              const docRef = doc(db, 'USERS', authUser.uid)
              const docSnap = await getDoc(docRef)
              const data = docSnap.data()    
              setUserData({...data, uid: authUser.uid}) // 存入uid用于后续判断
            } catch (error) {
              console.error(error)
            }
          }
        } else {
          setUserData(null)
        }
      })
      return () => unsubscribe()
    }, [userData]) // 添加userData作为依赖,确保判断逻辑生效
    
  • 缓存Context值避免重复渲染:当前authContextValue每次都会创建新对象,导致订阅useAuth的组件重复渲染。用useMemo缓存这个对象:

    const authContextValue = useMemo(() => ({    
      userData,        
      logIn,
      logOut,
      register,
    }), [userData]) // 仅当userData变化时更新对象引用
    
  • 查看Firestore控制台详情:直接去Firestore控制台的「使用情况」板块,查看每一条读取请求的具体路径和调用栈,精准定位重复请求的来源,比console.log更高效。

内容的提问来源于stack exchange,提问作者jose m ruiz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 22:28:10