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

Next Auth useSession钩子首次渲染无法获取会话问题求助

NextAuth v4 + Next.js 13 useSession首次渲染为null的问题分析与解决

问题场景

使用Next.js 13和NextAuth v4搭建网站时,调用useSession钩子获取会话,页面/组件首次渲染时session初始值为null,导致依赖会话的业务逻辑无法正常执行。

示例问题代码(QuestionPage)

'use client'
import { useEffect, useState } from 'react'
import { Question } from '@prisma/client'
import { useSession } from 'next-auth/react'

function QuestionPage() {
  const { data: session } = useSession() // 首次渲染时为null
  const [questions, setQuestions] = useState<Question[]>([])
  const [questionStatus, setQuestionStatus] = useState('');

  useEffect(() => {
    console.log('Session: ', session)
    if (!session) return;

    setQuestionStatus('Loading questions...');

    fetch('/api/questions')
        .then((res) => res.json())
        .then((data) => {
            setQuestions(data.questions)
            setQuestionStatus('');
        })
  }, []) // 依赖为空数组,仅执行一次

  return (
    <div>
      <p>{questionStatus}</p>
      {questions && questions.map(question => (
         <p key={question.id}>{question.title}</p>
      ))}
    </div>
  )
}

配套的Provider与Layout配置

ProvidersWrapper组件

'use client'
import QuestionContextWrapper from '@/context/QuestionContext'
import { SessionProvider } from 'next-auth/react'

export default function ProvidersWrapper({ children }: { children: React.ReactNode }) {
  return (
    <SessionProvider>
      <QuestionContextWrapper>
        {children}
      </QuestionContextWrapper>
    </SessionProvider>
  )
}

RootLayout组件

import './globals.css'
import Header from '@/components/Header'
import ProvidersWrapper from './ProvidersWrapper'
import Footer from '@/components/Footer'

export const metadata = {
  title: 'My app',
  description: 'My description',
}

export default function RootLayout({
  children,
}: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <ProvidersWrapper>
          <Header />
          {children}
          <Footer />
        </ProvidersWrapper>
      </body>
    </html>
  )
}

问题原因

  1. NextAuth会话初始化的异步性:SessionProvider在客户端初始化时,需要向服务端请求会话信息,这个过程是异步的。组件首次渲染时会话还未完成解析,useSession返回的data自然为null。
  2. useEffect依赖缺失:原代码中useEffect的依赖数组为空,只会在组件挂载时执行一次。此时session还处于未加载状态,直接跳过了请求逻辑,后续session加载完成后也不会重新触发请求。

解决方案

方案1:给useEffect添加session依赖

修改useEffect的依赖数组,加入session,这样每当session状态更新时,都会重新执行业务逻辑:

'use client'
import { useEffect, useState } from 'react'
import { Question } from '@prisma/client'
import { useSession } from 'next-auth/react'

function QuestionPage() {
  const { data: session } = useSession()
  const [questions, setQuestions] = useState<Question[]>([])
  const [questionStatus, setQuestionStatus] = useState('');

  useEffect(() => {
    if (!session) {
      setQuestionStatus('请先登录');
      return;
    }

    setQuestionStatus('Loading questions...');

    fetch('/api/questions')
        .then((res) => res.json())
        .then((data) => {
            setQuestions(data.questions)
            setQuestionStatus('');
        })
        .catch(() => {
            setQuestionStatus('加载失败');
        })
  }, [session]) // 添加session为依赖

  return (
    <div>
      <p>{questionStatus}</p>
      {questions.map(question => (
         <p key={question.id}>{question.title}</p>
      ))}
    </div>
  )
}

方案2:利用useSession的status状态判断加载状态

useSession还会返回status字段,值为loading/authenticated/unauthenticated,可以根据状态直接处理UI逻辑:

'use client'
import { useEffect, useState } from 'react'
import { Question } from '@prisma/client'
import { useSession } from 'next-auth/react'

function QuestionPage() {
  const { data: session, status } = useSession()
  const [questions, setQuestions] = useState<Question[]>([])
  const [questionStatus, setQuestionStatus] = useState('');

  useEffect(() => {
    if (status !== 'authenticated') return;

    setQuestionStatus('Loading questions...');

    fetch('/api/questions')
        .then((res) => res.json())
        .then((data) => {
            setQuestions(data.questions)
            setQuestionStatus('');
        })
  }, [status, session]) // 依赖status和session

  // 根据状态显示对应UI
  if (status === 'loading') return <p>加载中...</p>;
  if (status === 'unauthenticated') return <p>请登录后查看</p>;

  return (
    <div>
      <p>{questionStatus}</p>
      {questions.map(question => (
         <p key={question.id}>{question.title}</p>
      ))}
    </div>
  )
}

方案3:服务端预传初始会话(可选)

如果使用Next.js服务端组件,可以在服务端提前获取会话,传递给SessionProvider作为初始值,减少客户端等待时间:

  1. 修改ProvidersWrapper,接收服务端传递的session:
'use client'
import QuestionContextWrapper from '@/context/QuestionContext'
import { SessionProvider } from 'next-auth/react'
import { Session } from 'next-auth'

export default function ProvidersWrapper({ 
  children,
  session,
}: { 
  children: React.ReactNode,
  session?: Session
}) {
  return (
    <SessionProvider session={session}>
      <QuestionContextWrapper>
        {children}
      </QuestionContextWrapper>
    </SessionProvider>
  )
}
  1. 在RootLayout(服务端组件)中获取会话并传递:
import './globals.css'
import Header from '@/components/Header'
import ProvidersWrapper from './ProvidersWrapper'
import Footer from '@/components/Footer'
import { getServerSession } from 'next-auth/next'
import { authOptions } from '@/app/api/auth/[...nextauth]/route'

export const metadata = {
  title: 'My app',
  description: 'My description',
}

export default async function RootLayout({
  children,
}: { children: React.ReactNode }) {
  const session = await getServerSession(authOptions)
  return (
    <html lang="en">
      <body>
        <ProvidersWrapper session={session}>
          <Header />
          {children}
          <Footer />
        </ProvidersWrapper>
      </body>
    </html>
  )
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 12:55:03