Next Auth 的 useSession 在第一次渲染时未获取会话。

huangapple go评论133阅读模式
英文:

Next Auth useSession does not get session at first render

问题

I am trying to build a website using NextJS v13 and Next Auth v4. Whenever a page/component is loaded, the session is not returned in the first render when using useSession hook and because of this, many components don't work like they should...

For instance, the following code should load the questions for authenticated users:

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

function QuestionPage() {
  const { data: session } = useSession() // this is 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>
  )
}

This is my ProviderWrapper component that wraps the main app:

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

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

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

And this is the layout.tsx file:

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>
  )
}

Anyone knows why is next-auth behaving like this?

英文:

I am trying to build a website using NextJS v13 and Next Auth v4. Whenever a page/component is loaded, the session is not returned in the first render when using useSession hook and because of this, many components don't work like they should...

For instance, the following code should load the questions for authenticated users:

&#39;use client&#39;
import { useEffect, useState } from &#39;react&#39;
import { Question } from &#39;@prisma/client&#39;
import { useSession } from &#39;next-auth/react&#39;

function QuestionPage() {
  const { data: session } = useSession() // this is null
  const [questions, setQuestions] = useState&lt;Question[]&gt;([])
  const [questionStatus, setQuestionStatus] = useState(&#39;&#39;);

  useEffect(() =&gt; {
    console.log(&#39;Session: &#39;, session)
    if (!session) return;

    setQuestionStatus(&#39;Loading questions...&#39;);

    fetch(&#39;/api/questions&#39;)
        .then((res) =&gt; res.json())
        .then((data) =&gt; {
            setQuestions(data.questions)
            setQuestionStatus(&#39;&#39;);
        })
  }, [])

  return (
    &lt;div&gt;
      &lt;p&gt;{questionStatus}&lt;/p&gt;
      {questions &amp;&amp; questions.map(question =&gt; {
         &lt;p key={question.id}&gt;{question.title}&lt;/p&gt;
      })}
    &lt;/div&gt;
  )
}

This is my ProviderWrapper component that wraps the main app:

&#39;use client&#39;
import QuestionContextWrapper from &#39;@/context/QuestionContext&#39;
import { SessionProvider } from &#39;next-auth/react&#39;

export const metadata = {
  title: &#39;My App&#39;,
  description: &#39;My description&#39;,
}

export default function ProvidersWrapper({ children }: { children: React.ReactNode }) {
  return (
    &lt;SessionProvider&gt;
      &lt;QuestionContextWrapper&gt;
        {children}
      &lt;/QuestionContextWrapper&gt;
    &lt;/SessionProvider&gt;
  )
}

And this is the layout.tsx file:

import &#39;./globals.css&#39;
import Header from &#39;@/components/Header&#39;
import ProvidersWrapper from &#39;./ProvidersWrapper&#39;
import Footer from &#39;@/components/Footer&#39;

export const metadata = {
  title: &#39;My app&#39;,
  description: &#39;My description&#39;,
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    &lt;html lang=&quot;en&quot;&gt;
      &lt;body&gt;
        &lt;ProvidersWrapper&gt;
          &lt;Header /&gt;
          {children}
          &lt;Footer /&gt;
        &lt;/ProvidersWrapper&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  )
}

Anyone knows why is next-auth behaving like this?

答案1

得分: 2

经过一些研究,根据 @Thusithz 的回答,我找到了这个指南,以使用 Next Auth 和 TypeScript 实现正确的身份验证。因此,我将我的代码更改为以下内容,并且它运行正常。

我将 /src/components/PriverWrapper.tsx 重命名为 src/context/AuthProvider.tsx,并且也更改了代码如下:

'使用客户端'
import { Session } from 'next-auth'
import { SessionProvider } from 'next-auth/react'

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

export interface AuthContextProps {
  children: React.ReactNode
  session: Session
}

export default function AuthContext({ children }: AuthContextProps) {
  return <SessionProvider>{children}</SessionProvider>
}

然后将其应用于 /src/app/layout.tsx 文件,使用 AuthProvider 包装节点并传递 session 变量:

import './globals.css'
import Header from '@/components/Header'
import AuthProvider from '@/context/AuthContext'
import Footer from '@/components/Footer'
import { Session } from 'next-auth'

async function getSession(cookie: string): Promise<Session> {
  const response = await fetch(`${process.env.NEXTAUTH_URL}/session`, {
    headers: {
      cookie,
    }
  })

  const session = await response.json()

  return Object.keys(session).length > 0 ? session : null
}

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

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  const session = await getSession(headers().get('cookie') ?? '');
  return (
    <html lang="en">
      <body>
        <AuthContext session={session}>
          <Header />
          {children}
          <Footer />
        </AuthContext>
      </body>
    </html>
  )
}

因此,这按预期工作。

英文:

After some research and due to the @Thusithz's answer, I have found this guide to implement proper authentication using Next Auth and TypeScript. So I changed my code to the following and it worked.

I have renamed /src/components/PriverWrapper.tsx to src/context/AuthProvider.tsx and changed the code as well to the following:

&#39;use client&#39;
import { Session } from &#39;next-auth&#39;
import { SessionProvider } from &#39;next-auth/react&#39;

export const metadata = {
  title: &#39;My App&#39;,
  description: &#39;My description&#39;,
}

export interface AuthContextProps {
  children: React.ReactNode
  session: Session
}

export default function AuthContext({ children }: AuthContextProps) {
  return &lt;SessionProvider&gt;{children}&lt;/SessionProvider&gt;
}

And then applied this to the /src/app/layout.tsx file wrapping the node with AuthProvider and passing the session variable:

import &#39;./globals.css&#39;
import Header from &#39;@/components/Header&#39;
import AuthProvider from &#39;@/context/AuthContext&#39;
import Footer from &#39;@/components/Footer&#39;
import { Session } from &#39;next-auth&#39;

async function getSession(cookie: string): Promise&lt;Session&gt; {
  const response = await fetch(`${process.env.NEXTAUTH_URL}/session`, {
    headers: {
      cookie,
    }
  })

  const session = await response.json()

  return Object.keys(session).length &gt; 0 ? session : null
}

export const metadata = {
  title: &#39;My app&#39;,
  description: &#39;My description&#39;,
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  const session = await getSession(headers().get(&#39;cookie&#39;) ?? &#39;&#39;);
  return (
    &lt;html lang=&quot;en&quot;&gt;
      &lt;body&gt;
        &lt;AuthContext session={session}&gt;
          &lt;Header /&gt;
          {children}
          &lt;Footer /&gt;
        &lt;/AuthContext &gt;
      &lt;/body&gt;
    &lt;/html&gt;
  )
}

So, this works as expeted.

答案2

得分: 1

初始加载会话将未定义,因为它未传递给提供程序。尝试像下面这样将现有会话传递给提供程序。

export default function ProvidersWrapper({ children, session }: { children: React.ReactNode }) {
  return (
    <SessionProvider baseUrl={/* BASE_URL */} refetchInterval={60 * 5} session={session}>
      <QuestionContextWrapper>{children}</QuestionContextWrapper>
    </SessionProvider>
  );
}

ProvidersWrapper.getInitialProps = async (context) => {
  const { ctx } = context;
  const session = await getSession(ctx);

  return {
    session,
  };
};
英文:

Initial load session will be undefined since it's not pass to provider. Try to pass existing session to provider like below.

export default function ProvidersWrapper({ children, session }: { children: React.ReactNode }) {
return (
&lt;SessionProvider baseUrl={/* BASE_URL */} refetchInterval={60 * 5} session={session}&gt;
&lt;QuestionContextWrapper&gt;{children}&lt;/QuestionContextWrapper&gt;
&lt;/SessionProvider&gt;
);
}
ProvidersWrapper.getInitialProps = async (context) =&gt; {
const { ctx } = context;
const session = await getSession(ctx);
return {
session,
};
};

huangapple
  • 本文由 发表于 2023年5月30日 11:17:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76361411.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定