В настоящее время я создаю веб-сайт, используя 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)
Страница входа
Страница настроек после входа в систему с учетными данными (сессия недоступна)
Страница настроек после перезагрузки
Страница настроек после входа в систему с помощью 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;
};
Я исправил ошибку, перезагрузив страницу после нажатия кнопки отправки при входе в систему.
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!"));
});
};