NextJS 13.0.6+TypeScript+Firebase中AuthContext未捕获TypeError
问题:Next.js 13 + TypeScript 中使用 AuthContext 时出现
useUserAuth is not a function 错误 我想实现用户登录后才显示页面的功能,当前应用只有 AuthContext 和目标页面,代码在 React 中能正常运行,但迁移到 TypeScript 和 Next.js 13 后报错,我对这两者都不熟悉,求帮忙解决。
AuthContext.tsx
'use client'; import { useContext, createContext, useEffect, useState } from 'react'; import { GoogleAuthProvider, signInWithPopup, signInWithRedirect, signOut, onAuthStateChanged, signInWithEmailAndPassword, User, } from 'firebase/auth'; import { auth } from '../../../firebaseConfig'; import getUserData from '../api/getUserData'; //-----------------------interfaces----------------------------------------- interface Props { children: React.ReactNode; } interface SignInCredentials { email: string; password: string; } export interface AuthContextValues { googleSignIn: () => void; emailSignIn: ({ email, password }: SignInCredentials) => void; logout: () => void; user: User | null; userProfileData: any; loadingUser: boolean; } //-----------------AuthContextProvider-------------------------------------- const emailSignIn = ({ email, password }: SignInCredentials) => { try { signInWithEmailAndPassword(auth, email, password); } catch (e) { console.error(e); } }; const googleSignIn = () => { const provider = new GoogleAuthProvider(); signInWithRedirect(auth, provider); }; const initialState: AuthContextValues = { user: null, googleSignIn, emailSignIn, logout: function (): void { throw new Error('Function not implemented.'); }, userProfileData: undefined, loadingUser: false, }; const AuthContext = createContext<AuthContextValues>(initialState); export const AuthContextProvider = ({ children }: Props) => { const [user, setUser] = useState<User | null>(null); const [userProfileData, setUserProfileData] = useState(null); const [loadingUser, setLoadingUser] = useState<boolean>(true); const logout = async () => { await signOut(auth); setUser(null); setUserProfileData(null); }; useEffect(() => { const unsubscribe = onAuthStateChanged(auth, async (currentUser) => { try { if (currentUser) { setUser(currentUser); await getUserData(currentUser, setUserProfileData); } else { setUser(null); console.log('no user detected in onAuthStateChanged'); } } catch (e) { console.error(e); // toast.error('There was a problem signing in. Please try again.') } finally { setLoadingUser(false); } }); return () => { unsubscribe(); }; }, []); console.log(user); console.log(userProfileData); return ( <AuthContext.Provider value={{ googleSignIn, emailSignIn, logout, user, userProfileData, loadingUser, }} > {children} </AuthContext.Provider> ); }; export const useUserAuth = () => { return useContext(AuthContext); };
page.tsx(用户页面)
import React from 'react'; import { useUserAuth } from './../../components/shared/context/AuthContext'; type Props = {}; export default function UserPage(props: Props) { const { user } = useUserAuth(); return <> {user?.uid} </>; }
layout.tsx(根布局)
import './global.css'; import { AuthContextProvider } from './../components/shared/context/AuthContext'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang='en'> <body> <AuthContextProvider>{children}</AuthContextProvider> </body> </html> ); }
错误信息
client.js?234c:691 Uncaught TypeError: (0 , _components_shared_context_AuthContext__WEBPACK_IMPORTED_MODULE_2__.useUserAuth) is not a function at UserPage (webpack-internal:///(sc_server)/./app/user/page.tsx:13:106) at attemptResolveElement (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1207:42) at resolveModelToJSON (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1660:53) at Array.toJSON (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1121:40) at stringify (<anonymous>) at processModelChunk (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:172:36) at retryTask (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1868:50) at performWork (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1906:33) at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1297:40) at scheduleWork (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:52:25) at pingTask (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1296:29) at ping (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/react-server-dom-webpack/server.browser.js:1309:40) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) resolveErrorDev @ client.js?234c:691 processFullRow @ client.js?234c:756 processBinaryChunk @ client.js?234c:789 progress @ client.js?234c:842
已尝试的方法
- 重命名
useUserAuth,无效 - 原无 TypeScript 的代码在 React 18 中正常运行
解决方法
这个错误的核心原因是 Next.js 13 的 App Router 默认采用 Server Components,而你在 Server Component 中调用了只能在 Client Component 中使用的自定义 Hook(useUserAuth 依赖 React Context,属于 Client 侧 API)。
步骤1:将用户页面标记为 Client Component
在 page.tsx 顶部添加 'use client'; 指令,告诉 Next.js 这个组件需要在客户端运行:
'use client'; // 新增这一行 import React from 'react'; import { useUserAuth } from './../../components/shared/context/AuthContext'; type Props = {}; export default function UserPage(props: Props) { const { user } = useUserAuth(); return <> {user?.uid} </>; }
步骤2:优化 AuthContext 的类型定义(可选但推荐)
当前 userProfileData 的类型是 any,可以换成更明确的类型来发挥 TypeScript 的优势。比如先定义一个用户数据接口:
// 在 AuthContext.tsx 中新增 interface UserProfileData { // 根据你的实际数据结构定义字段,比如: id: string; name: string; // ...其他字段 } // 然后修改 AuthContextValues 和 useState 的类型 export interface AuthContextValues { // ...其他字段 userProfileData: UserProfileData | null; // 替换 any } // 在 AuthContextProvider 中 const [userProfileData, setUserProfileData] = useState<UserProfileData | null>(null);
步骤3:处理 loading 状态(可选)
当前页面在用户加载完成前会直接显示 undefined,可以利用 loadingUser 状态添加加载提示:
// 修改 UserPage export default function UserPage(props: Props) { const { user, loadingUser } = useUserAuth(); if (loadingUser) { return <div>加载中...</div>; } if (!user) { // 若用户未登录,可以跳转到登录页或显示提示 return <div>请先登录</div>; } return <> {user.uid} </>; }
内容的提问来源于stack exchange,提问作者Tom Ash
相关产品推荐
相关产品推荐

