NextJS中getServerSideProps调用UseContext返回null报错求助
解决Next.js中getServerSideProps调用Auth Context报错的问题
问题说明
在登录、数据库交互等客户端场景中能正常使用Auth Context,但在getServerSideProps或自定义API路由中调用UseAuth()时,出现报错:
TypeError: Cannot read properties of null (reading 'useContext')
报错原因
- React的
useContext是客户端专属的React钩子,只能在React组件或自定义钩子的顶层调用,不能在服务器端的非React上下文环境中执行。 UseAuth()本质是封装了useContext调用,而getServerSideProps是Next.js的服务器端渲染函数(运行在Node.js环境,无React上下文),自定义API路由同样是服务器端的Node.js环境,直接调用UseAuth()必然触发上下文不存在的错误。
解决方案
场景1:在getServerSideProps中获取用户信息
需要通过请求的Cookie获取Firebase认证的会话信息,用Firebase Admin SDK验证并拿到用户数据:
- 安装Firebase Admin SDK
npm install firebase-admin
- 配置Firebase Admin SDK
在项目根目录创建firebase-admin.js:
import admin from 'firebase-admin'; if (!admin.apps.length) { admin.initializeApp({ credential: admin.credential.cert({ projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, clientEmail: process.env.FIREBASE_CLIENT_EMAIL, privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n'), }), databaseURL: `https://${process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID}.firebaseio.com`, }); } export default admin;
- 修改getServerSideProps逻辑
import admin from '../firebase-admin'; export async function getServerSideProps(context) { let allDocs = []; let avgDocs = []; let currentUser = null; // 从请求Cookie中提取Firebase会话Cookie const sessionCookie = context.req.cookies.__session; if (sessionCookie) { try { // 验证会话Cookie,获取用户信息 const decodedClaims = await admin.auth().verifySessionCookie(sessionCookie, true); currentUser = { uid: decodedClaims.uid, email: decodedClaims.email }; // 这里可以用currentUser查询数据库,填充allDocs和avgDocs // 示例: // const userDoc = await admin.firestore().doc(`users/${currentUser.uid}`).get(); // if (userDoc.exists()) { // // 处理数据... // } } catch (error) { // Cookie无效/过期,跳转登录页 return { redirect: { destination: '/', permanent: false, }, }; } } else { // 无会话Cookie,跳转登录页 return { redirect: { destination: '/', permanent: false, }, }; } return { props: { allDocs, avgDocs, currentUser }, }; }
- 客户端登录时设置会话Cookie
修改auth-context.js中的登录函数,添加会话Cookie设置:
async function login(email, password) { return signInWithEmailAndPassword(auth, email, password) .then(async (result) => { const user = result.user; // 获取ID Token并创建会话Cookie const idToken = await user.getIdToken(); const expiresIn = 60 * 60 * 24 * 5; // 有效期5天 const sessionCookie = await auth.createSessionCookie(idToken, { expiresIn }); // 设置Cookie(生产环境开启secure) document.cookie = `__session=${sessionCookie}; max-age=${expiresIn}; path=/; ${process.env.NODE_ENV === 'production' ? 'secure; ' : ''}HttpOnly; SameSite=Lax`; await userToDb(user); router.push('/portfolio'); return user; }).catch((error) => { console.error(error); }); }
同时修改logout函数清除Cookie:
function logout() { // 清除会话Cookie document.cookie = '__session=; max-age=0; path=/'; router.push('/'); return signOut(auth); }
场景2:在自定义API路由中获取用户信息
逻辑和getServerSideProps一致,用Admin SDK验证Cookie:
// pages/api/your-api.js import admin from '../../firebase-admin'; export default async function handler(req, res) { const sessionCookie = req.cookies.__session; if (!sessionCookie) { return res.status(401).json({ message: '未授权' }); } try { const decodedClaims = await admin.auth().verifySessionCookie(sessionCookie, true); const currentUser = { uid: decodedClaims.uid, email: decodedClaims.email }; // 执行你的API业务逻辑 res.status(200).json({ user: currentUser }); } catch (error) { res.status(401).json({ message: '认证失效' }); } }
注意事项
- 禁止在服务器端的非React环境中调用任何React钩子(useContext、useState等),这违反React钩子的使用规则。
- Firebase Admin的配置信息(clientEmail、privateKey)需存储在环境变量中,切勿硬编码到代码里。
- 生产环境必须开启Cookie的
secure属性,确保通过HTTPS传输。
内容的提问来源于stack exchange,提问作者Dotman
相关产品推荐
相关产品推荐

