Auth.js v5 useSession(): данные сеанса отображаются только после перезагрузки страницы вручную (NextJS 14)

В настоящее время я создаю веб-сайт, используя NextJS 14 и Auth.js v5. Моя проблема - страница настроек. После входа пользователя он перенаправляется на страницу настроек. В зависимости от того, входит ли пользователь в систему с помощью OAuth, например Github, или с помощью учетных данных, он может изменить больше или меньше настроек (например, 2FA...). Но на данный момент после входа в систему с учетными данными сеанс не обновляется автоматически, и мне приходится обновлять веб-сайт, чтобы увидеть явные данные сеанса. При использовании OAuth все работает отлично. Я думаю, что это проблема с перехватчиком useSession из auth.js, но я не могу понять, как решить эту проблему. Я все еще относительно новичок в NextJS и Auth.js, поэтому простите меня, если язык немного неточный. Буду рад рассказать подробнее, если что-то будет неясно. Изображения и код приведены ниже... (полный код можно найти на Github: https://github.com/EnXan/UTP ). Я использовал код из этого урока, в котором он работает как положено: https://github.com/Hombre2014/nextjs-14-auth-v5-tutorial)

  1. Страница входа

  2. Страница настроек после входа в систему с учетными данными (сессия недоступна)

  3. Страница настроек после перезагрузки

  4. Страница настроек после входа в систему с помощью Github (нет необходимости обновлять страницу, здесь все работает)

настройки (page.tsx)

"use client";

import * as z from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useTransition, useState } from "react";
import { useSession } from "next-auth/react";

import { Switch } from "@/components/ui/switch";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { SettingsSchema } from "@/schemas";
import { Card, CardHeader, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { settings } from "@/actions/settings";
import { Form, FormField, FormControl, FormItem, FormLabel, FormDescription, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { useCurrentUser } from "@/hooks/use-current-user";
import { FormError } from "@/components/form-error";
import { FormSuccess } from "@/components/form-success";
import { UserRole } from "@prisma/client";

const SettingsPage = () => {
  const user = useCurrentUser();
  const session = useSession();

  console.info(session);
  console.info("User Data:", user);
  console.info("isOAuth:", user?.isOAuth);

  const [error, setError] = useState<string | undefined>();
  const [success, setSuccess] = useState<string | undefined>();
  const { update } = useSession();
  const [isPending, startTransition] = useTransition();

  const form = useForm<z.infer<typeof SettingsSchema>>({
    resolver: zodResolver(SettingsSchema),
    defaultValues: {
      password: undefined,
      newPassword: undefined,
      name: user?.name || undefined,
      email: user?.email || undefined,
      role: user?.role || undefined,
      isTwoFactorEnabled: user?.isTwoFactorEnabled || undefined,
    },
  });

  const onSubmit = (values: z.infer<typeof SettingsSchema>) => {
    startTransition(() => {
      settings(values)
        .then((data) => {
          if (data.error) {
            setError(data.error);
          }

          if (data.success) {
            update();
            setSuccess(data.success);
          }
        })
        .catch(() => setError("Something went wrong!"));
    });
  };

  return (
    <Card className = "w-[600px]">
      <CardHeader>
        <p className = "text-center text-2xl font-semibold">⚙️ Settings</p>
      </CardHeader>
      <CardContent>
        <Form {...form}>
          <form className = "space-y-6" onSubmit = {form.handleSubmit(onSubmit)}>
            <div className = "space-y-4">
              <FormField
                control = {form.control}
                name = "name"
                render = {({ field }) => (
                  <FormItem>
                    <FormLabel>Name</FormLabel>
                    <FormControl>
                      <Input {...field} placeholder = "John Doe" disabled = {isPending} />
                    </FormControl>
                    <FormMessage />
                  </FormItem>
                )}
              />
              {user?.isOAuth === false && (
                <>
                  <FormField
                    control = {form.control}
                    name = "email"
                    render = {({ field }) => (
                      <FormItem>
                        <FormLabel>Email</FormLabel>
                        <FormControl>
                          <Input {...field} placeholder = "[email protected]" type = "email" disabled = {isPending} />
                        </FormControl>
                        <FormMessage />
                      </FormItem>
                    )}
                  />
                  <FormField
                    control = {form.control}
                    name = "password"
                    render = {({ field }) => (
                      <FormItem>
                        <FormLabel>Password</FormLabel>
                        <FormControl>
                          <Input {...field} placeholder = "******" type = "password" disabled = {isPending} />
                        </FormControl>
                        <FormMessage />
                      </FormItem>
                    )}
                  />
                  <FormField
                    control = {form.control}
                    name = "newPassword"
                    render = {({ field }) => (
                      <FormItem>
                        <FormLabel>New Password</FormLabel>
                        <FormControl>
                          <Input {...field} placeholder = "******" type = "password" disabled = {isPending} />
                        </FormControl>
                        <FormMessage />
                      </FormItem>
                    )}
                  />
                </>
              )}
              <FormField
                control = {form.control}
                name = "role"
                render = {({ field }) => (
                  <FormItem>
                    <FormLabel>Role</FormLabel>
                    <Select disabled = {isPending} onValueChange = {field.onChange} defaultValue = {field.value}>
                      <FormControl>
                        <SelectTrigger>
                          <SelectValue placeholder = "Select a role" />
                        </SelectTrigger>
                      </FormControl>
                      <SelectContent>
                        <SelectItem value = {UserRole.ADMIN}>Admin</SelectItem>
                        <SelectItem value = {UserRole.USER}>User</SelectItem>
                      </SelectContent>
                    </Select>
                    <FormMessage />
                  </FormItem>
                )}
              />
              {user?.isOAuth === false && (
                <FormField
                  control = {form.control}
                  name = "isTwoFactorEnabled"
                  render = {({ field }) => (
                    <FormItem className = "flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
                      <div className = "space-y-0.5">
                        <FormLabel>Two Factor Authentication</FormLabel>
                        <FormDescription>Enable two factor authentication for your account</FormDescription>
                      </div>
                      <FormControl>
                        <Switch disabled = {isPending} checked = {field.value} onCheckedChange = {field.onChange} />
                      </FormControl>
                    </FormItem>
                  )}
                />
              )}
            </div>
            <FormError message = {error} />
            <FormSuccess message = {success} />
            <Button disabled = {isPending} type = "submit">
              Save
            </Button>
          </form>
        </Form>
      </CardContent>
    </Card>
  );
};

export default SettingsPage;

Корневой макет (layout.tsx)

import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { auth } from "@/auth";
import { SessionProvider } from "next-auth/react";
import "./globals.css";
import React from "react";
import { Toaster } from "@/components/ui/sonner";
import Navbar from "@/components/navigation/navbar/navbar";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default async function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  const session = await auth();

  return (
    <SessionProvider session = {session}>
      <html lang = "en" suppressHydrationWarning>
        <body className = {` ${inter.className}`}>
          <Navbar session = {session} />
          <Toaster />
          <div className = "pt-5">{children}</div>
        </body>
      </html>
    </SessionProvider>
  );
}

useCurrentUser-Hook

"use client";

import { useSession } from "next-auth/react";

export const useCurrentUser = () => {
  const session = useSession();

  return session.data?.user;
};
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
0
0
84
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я исправил ошибку, перезагрузив страницу после нажатия кнопки отправки при входе в систему.

  const onSubmit = (values: z.infer<typeof LoginSchema>) => {
    setError("");
    setSuccess("");

    startTransition(() => {
      login(values)
        .then((data) => {
          window.location.reload();

          if (data?.error) {
            form.reset();
            setError(data.error);
          }

          if (data?.success) {
            form.reset();
            setSuccess(data.success);
            update();
          }

          if (data?.twoFactor) {
            setShowTwoFactor(true);
          }
        })
        .catch(() => setError("An error occurred!"));
    });
  };

Другие вопросы по теме

Role_required декоратор для маршрута FastAPI
Проблема несоответствия пароля с bcryptjs в Angular, NodeJS и MySQL
Укажите срок действия при запросе токена доступа при входе в Microsoft oauth2 v2.0
Кто-нибудь знает, почему моя переменная (x) изменила свое значение с 3 на 0 (1 после x++) в этой программе? Я пытаюсь создать систему регистрации, но она перезаписывает
Можете ли вы создать «Потоки пользователей» в Azure с помощью учетной записи с оплатой по мере использования?
Я пытаюсь создать систему входа в систему на языке C, которая скрывает пароль с помощью '*' при вводе и возвращается назад, когда пользователь вводит неверную информацию
Oauth2-proxy `/oauth2/auth` возвращает 401 для действительных токенов JWT
Реализация криптохэша – Node.js 22 – Angular 18
При попытке аутентификации Firebase мне сообщается, что сначала мне нужно пройти аутентификацию Firebase. Хм?
Измените конфигурацию для регистрации приложения, чем для корпоративного приложения