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

React应用中UserAuth返回undefined无法解构user的问题求助

问题描述

开发React应用时,App组件无法获取已存在的user值,但其他组件使用相同语法可正常运行。报错信息:

Uncaught TypeError: Cannot destructure property 'user' of '(0 , context_AuthContext__WEBPACK_IMPORTED_MODULE_2_.UserAuth)(...)' as it is undefined

附相关代码:

App组件代码

import React, { useState, useEffect } from "react";
import { Drawer, Login, Logout, Signup, ForgotPassword, UpdateProfile, JoinedClasses } from "./components";
import { BrowserRouter as Router, Routes, Route} from "react-router-dom";
import { UserAuth, AuthProvider } from "./context/AuthContext";
import PrivateRoutes from "./Routes/PrivateRoutes";
import { db } from './FireBase/Firebase'
import { doc, collection, onSnapshot } from 'firebase/firestore';

function App() {

  const { user } = UserAuth() 
  const [createdClasses, setCreatedClasses] = useState([])
  const [joinedClasses, setJoinedClasses] = useState([]);


  console.log(user)
  useEffect(() => {

    if (user) {
      const createdClassesRef = collection(doc(db, "Created Classes", user), "Classes");
      const unsubscribe = onSnapshot(createdClassesRef, (snapshot) => {
        setCreatedClasses(snapshot.docs.map((doc) => doc.data()));
      });
      return () => unsubscribe();
    }
  }, [user]);
  
  console.log(createdClasses)

  useEffect(() => {
    if (user) {
      const joinedClassesRef = collection(doc(db, "JoinedClasses", user), "classes");
      const unsubscribe = onSnapshot(joinedClassesRef, (snapshot) => {
        setJoinedClasses(snapshot.docs.map((doc) => doc.data().joinedData));
      });
      return () => unsubscribe();
    }
  }, [user]);

  console.log(joinedClasses)

  return (
    <Router>
      <AuthProvider>
        <Routes>
          <Route exact path="/" element={
          <PrivateRoutes>
            <Drawer />
            <ol>
              {createdClasses.map((item) => (
                <JoinedClasses classData={item} />
              ))}
              {joinedClasses.map((item) => (
                <JoinedClasses classData={item} />
              ))}
            </ol>
            </PrivateRoutes>} />
          <Route path="/update-profile" element={<PrivateRoutes><UpdateProfile /></PrivateRoutes>} />
          <Route path="/logout" element={<PrivateRoutes><Logout /></PrivateRoutes>} />
          <Route path="/login" element={<Login />} />
          <Route path="/signup" element={<Signup />} />
          <Route path="/forgot-password" element={<ForgotPassword />} />
        </Routes>
      </AuthProvider>
    </Router>
  );
}

export default App;

AuthContext代码

import React, { createContext, useContext, useState, useEffect } from "react"
import { auth } from "../FireBase/Firebase"
import { createUserWithEmailAndPassword, signInWithEmailAndPassword, signOut, updateEmail, updatePassword } from "firebase/auth"
import { GoogleAuthProvider, signInWithRedirect, sendPasswordResetEmail } from "firebase/auth";

const AuthContext = createContext()

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState({})
  // const [user, setUser] = useState(null)
  const [loading, setLoading] = useState(true)

  const googleSignIn = () => {
    const provider = new GoogleAuthProvider();
    // signInWithPopup(auth, provider)
    signInWithRedirect(auth, provider)
  }

  const createUser = (email, password) => {
    return createUserWithEmailAndPassword(auth, email, password)
  }

  const signIn= (email, password) => {
    return signInWithEmailAndPassword(auth, email, password)
  }

  const logout = () => {
    return signOut(auth)
  }

  const resetPassword = (email) => {
    return sendPasswordResetEmail(auth, email)
  }

  const newEmail= (email) => {
    return updateEmail(user, email)
  }

  const newPassword = (password) => {
    return updatePassword(user, password)
  }

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged(currentUser => {
      setUser(currentUser)
      setLoading(false)
      console.log('User', currentUser)
    })

    return unsubscribe
  }, [])

  const value = {
    user,
    createUser,
    googleSignIn,
    signIn,
    logout,
    resetPassword,
    newEmail,
    newPassword
  }

  return (
    <AuthContext.Provider value={value}>
      {!loading && children}
    </AuthContext.Provider>
  )
  
}

export const UserAuth = () => {
  return useContext(AuthContext)
}

解决方案

核心问题

App组件的业务逻辑(包括调用UserAuth())不在AuthProvider的上下文覆盖范围内。因为AuthProvider是在App组件的return方法中渲染的,而App组件本身在return之外执行的代码,此时还没有被AuthProvider包裹,无法获取上下文值。

修复步骤

  1. 调整组件层级,将AuthProvider提到App组件外部
    修改项目的index.js,让AuthProvider直接包裹App组件,确保App的所有代码都处于上下文范围内:

    // index.js
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import App from './App';
    import { AuthProvider } from './context/AuthContext';
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      <React.StrictMode>
        <AuthProvider>
          <App />
        </AuthProvider>
      </React.StrictMode>
    );
    
  2. 移除App组件中的AuthProvider
    修改App组件的return部分,删除<AuthProvider>标签:

    // App.js
    function App() {
      const { user } = UserAuth();
      // 其余逻辑保持不变...
    
      return (
        <Router>
          <Routes>
            {/* 路由内容保持不变 */}
          </Routes>
        </Router>
      );
    }
    

额外优化建议

  • 修正Firestore文档ID的使用:Firebase用户对象不能直接作为文档ID,应该使用user.uid,比如:
    const createdClassesRef = collection(doc(db, "Created Classes", user.uid), "Classes");
    
  • 设置user初始值为null:在AuthContext中将useState({})改为useState(null),这样空值判断更准确:
    const [user, setUser] = useState(null)
    
  • 增加空值保护:在使用user属性时使用可选链操作符,避免报错:
    useEffect(() => {
      if (user?.uid) {
        // 执行Firestore操作
      }
    }, [user]);
    

内容的提问来源于stack exchange,提问作者Ausku

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 02:07:01