在App.jsx中访问AuthDataContext报错,请求排查问题原因
问题描述
在App.jsx中尝试从AuthDataContext获取用户数据时,出现错误:
Uncaught TypeError: Cannot destructure property 'user' of '(0 , react__WEBPACK_IMPORTED_MODULE_0__.useContext)(...)' as it is null
用户已用AuthContext包裹组件,但仍无法访问上下文。
核心原因
你的App组件是AuthContext的父级组件,而非子组件。React Context的规则是:只有被Provider包裹的子组件才能通过useContext获取到上下文数据。当前App组件在AuthContext.Provider的外层,自然拿不到上下文值,只能拿到createContext初始化的null。
另外,原AuthContext组件存在一个小问题:onAuthStateChanged直接写在组件顶层,会导致每次组件渲染都重复注册监听,引发不必要的重渲染和潜在内存泄漏。
解决方案
步骤1:修复AuthContext的监听逻辑
将onAuthStateChanged放入useEffect中,确保只注册一次监听,并在组件卸载时清理:
// Authentication Context import { createContext, useState, useEffect } from "react"; import { getAuth, onAuthStateChanged } from "firebase/auth"; export const AuthDataContext = createContext(null); export default function AuthContext({ children }) { const auth = getAuth(); const [user, setUser] = useState(null); useEffect(() => { // 注册auth状态变更监听 const unsubscribe = onAuthStateChanged(auth, (user) => { setUser(user ?? null); }); // 组件卸载时取消监听,避免内存泄漏 return unsubscribe; }, [auth]); return ( <AuthDataContext.Provider value={{ user, setUser }}> {children} </AuthDataContext.Provider> ); }
步骤2:将AuthContext的包裹层级移到App外层
修改项目入口文件(通常是index.js),用AuthContext包裹整个App,让App成为Context的子组件:
// index.js import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import AuthContext from './context/Auth-Context'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <AuthContext> <App /> </AuthContext> </React.StrictMode> );
步骤3:调整App组件的结构
移除App组件内部的AuthContext包裹,现在App已处于Context的覆盖范围内,可以正常使用useContext:
// App.jsx ...other code import ContextProviders from "./context/Context-Config"; import { AuthDataContext } from "./context/Auth-Context"; import { useContext } from 'react'; export default function App() { ...other code // 现在可以正常获取上下文数据 const { user, setUser } = useContext(AuthDataContext); return ( <ContextProviders configs={configs} genres={{ genresMovie, genresTv, }} bookmarkShows={{ bookmarkShows, setBookmarkShows, bookmarksTrace, setBookmarksTrace, }} movies={{ trendingMovies, popularMovies, topRatedMovies, upcomingMovies, nowPlayingMovies, }} tv={{ popularTv, topRatedTv, airingTodayTv, onTheAirTv, }} > <div className="App"> <Navbar /> <SearchBar /> <Outlet /> </div> </ContextProviders> ); }
替代方案
如果不想修改入口文件,也可以将需要使用user的逻辑移到App的子组件(比如Navbar、SearchBar)中,这些组件本来就在AuthContext的覆盖范围内,直接在子组件中使用useContext即可。
内容的提问来源于stack exchange,提问作者rigan

