我创建了一个样板,其中有一个指向相应路线的简单导航菜单。该菜单是在layout.tsx 中的页眉组件中创建的,因为它应该出现在所有页面上。
布局包含
<Header>
,其中包含
<NavWithMobile>
,其中包含
<AuthButton>
AuthButton
根据 Supabase 用户的存在有条件地呈现自身,并且是路由到
/login
的登录按钮,或者从
use server
指定文件调用 Supabase 注销函数的注销按钮。AuthButton 中对用户的检查发生在 useEffect 挂钩内,以允许在客户端组件中使用 wait。同样在 useEffect 挂钩中,设置了上下文。布局包含在上下文中。
登录和注销是有效的,但是,除非刷新浏览器,否则 AuthButton 组件不会更新其状态。事实上,AuthButton 在浏览器刷新后会更新其状态,这表明上下文已成功使用。
所有 Supabase 异步函数(登录、注销)都位于
use server
指定的单独文件中,以便可以从客户端组件调用它们。它由以下部分组成:
布局.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Header from "@/components/Header";
import { UserContextWrapper } from '@/context/user';
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Texans Giving Back",
description: "Non Profit Dontations App",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
<UserContextWrapper>
<Header />
<main className="flex min-h-screen flex-col items-center justify-center">
{children}
</main>
</UserContextWrapper>
</body>
</html>
);
}
标头.tsx
import React from 'react';
import NavWithMobile from "../components/NavWithMobile";
const Header: React.FC = () => {
return (
<header>
<NavWithMobile />
</header>
);
};
export default Header;
NavWithMobile.tsx
"use client";
import React, { useState } from 'react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import DottedNavDivider from "./DottedNavDivider";
import HamburgerMenuBtn from "./HamburgerMenuBtn";
import CloseBtnX from "./CloseBtnX";
import AuthButton from "@/components/AuthButton";
const NavWithMobile: React.FC<Props> = () => {
const pathname = usePathname();
const [menuIsOpen, setMenuIsOpen] = useState(false);
const toggleMenu = () => {
setMenuIsOpen(prev => !prev);
}
return (
<>
<nav className="relative px-4 py-4 flex justify-end items-center bg-white lg:py-6">
<div className={`${menuIsOpen ? 'hidden' : 'block'} lg:hidden`} onClick={toggleMenu}>
<button className="navbar-burger flex items-center text-blue-600 ">
<HamburgerMenuBtn cn="block h-4 w-4 fill-current" vb="0 0 20 20" />
</button>
</div>
<ul className="hidden absolute whitespace-nowrap top-1/2 left-1/2 transform -translate-y-1/2 -translate-x-1/2 lg:flex lg:mx-auto lg:items-center lg:w-auto lg:space-x-6 lg:pr-4">
<li>
<Link className={`${pathname == '/' ? 'active' : ''} text-sm text-gray-400 hover:text-gray-500`} href="/">Home</Link>
</li>
<li className="text-gray-300">
<DottedNavDivider cn="w-4 h-4 current-fill" vb="0 0 24 24" />
</li>
<li>
<Link className={`${pathname == '/about' ? 'active' : ''} text-sm text-gray-400 whitespace-nowrap`} href="/about-me">About Me</Link>
</li>
<li className="text-gray-300">
<DottedNavDivider cn="w-4 h-4 current-fill" vb="0 0 24 24" />
</li>
<li>
<Link className={`${pathname == '/services' ? 'active' : ''} text-sm text-gray-400 hover:text-gray-500`} href="/about-site">About This Site</Link>
</li>
<li className="text-gray-300">
<DottedNavDivider cn="w-4 h-4 current-fill" vb="0 0 24 24" />
</li>
<li>
<Link className={`${pathname == '/pricing' ? 'active' : ''} text-sm text-gray-400 hover:text-gray-500`} href="/portfolio">Portfolio</Link>
</li>
<li className="text-gray-300">
<DottedNavDivider cn="w-4 h-4 current-fill" vb="0 0 24 24" />
</li>
<li>
<Link className={`${pathname == '/contact' ? 'active' : ''} text-sm text-gray-400 hover:text-gray-500`} href="/contact">Get in Touch</Link>
</li>
</ul>
<AuthButton />
</nav>
<div className={`${menuIsOpen ? 'block' : 'hidden'} navbar-menu relative z-50`} >
<div className="navbar-backdrop fixed inset-0 bg-gray-800 opacity-25"></div>
<nav className="fixed top-0 left-0 bottom-0 flex flex-col w-5/6 max-w-sm py-6 px-6 bg-white border-r overflow-y-auto">
<div className="flex items-center mb-8">
<button className="navbar-close" onClick={toggleMenu}>
<CloseBtnX cn={'h-6 w-6 text-gray-400 cursor-pointer hover:text-gray-500'} vb='0 0 24 24' />
</button>
</div>
<div onClick={toggleMenu}>
<ul>
<li className="mb-1">
<Link className={`${pathname == '/' ? 'active' : ''} block p-4 text-sm font-semibold text-gray-400 hover:bg-blue-50 hover:text-blue-600 rounded`} href="/">Home</Link>
</li>
<li className="mb-1">
<Link className={`${pathname == '/about' ? 'active' : ''} block p-4 text-sm font-semibold text-gray-400 hover:bg-blue-50 hover:text-blue-600 rounded`} href="/about-me">About Me</Link>
</li>
<li className="mb-1">
<Link className={`${pathname == '/services' ? 'active' : ''} block p-4 text-sm font-semibold text-gray-400 hover:bg-blue-50 hover:text-blue-600 rounded`} href="/about-site">About This Site</Link>
</li>
<li className="mb-1">
<Link className={`${pathname == '/pricing' ? 'active' : ''} block p-4 text-sm font-semibold text-gray-400 hover:bg-blue-50 hover:text-blue-600 rounded`} href="/portfolio">Portfolio</Link>
</li>
<li className="mb-1">
<Link className={`${pathname == '/contact' ? 'active' : ''} block p-4 text-sm font-semibold text-gray-400 hover:bg-blue-50 hover:text-blue-600 rounded`} href="/contact">Get in Touch</Link>
</li>
</ul>
</div>
<div className="mt-auto">
<div className="pt-6">
<AuthButton />
</div>
</div>
</nav>
</div>
</>
);
};
export default NavWithMobile;
AuthButton.tsx
"use client";
import { signOut, getUser } from "@/utils/supabase/supabaseAuth";
import Link from "next/link";
import { useEffect, useState } from 'react';
import { useUserContext } from '@/context/user';
export default function AuthButton() {
const { userContext, setUserContext } = useUserContext();
useEffect(() => {
const init = async () => {
const _user = await getUser();
setUserContext(_user);
};
init();
}, []);
return userContext ? (
<div className="flex items-center gap-4">
{userContext.email}
<form action={signOut}>
<button className="py-2 px-4 rounded-md no-underline bg-btn-background hover:bg-btn-background-hover">
Logout
</button>
</form>
</div>
) : (
<Link
href="/login"
className="py-2 px-3 flex rounded-md no-underline bg-btn-background hover:bg-btn-background-hover"
>
Login
</Link>
);
}
/登录/page.tsx
import React from 'react';
import UserAuthForm from "@/components/UserAuthForm";
const Login: React.FC = () => {
return (
<>
<div className="min-h-screen p-6 bg-gray-100 flex justify-center">
<div className="container max-w-screen-lg mx-auto">
<div>
<div className="bg-white rounded shadow-lg p-4 px-4 md:p-8 mb-6">
<UserAuthForm />
</div>
</div>
</div>
</div>
</>
);
};
export default Login;
UserAuthForm.tsx
"use client";
import { useRouter } from 'next/navigation';
import Link from "next/link";
import { headers } from "next/headers";
import { createClient } from "@/utils/supabase/server";
import { redirect } from "next/navigation";
import { SubmitButton } from "@/components/submit-button";
import { signIn, signUp } from "@/utils/supabase/supabaseAuth";
export default function UserAuthForm({
searchParams,
}: {
searchParams: { message: string };
}) {
const router = useRouter();
return (
<>
<div className="grid gap-y-2 text-sm md:grid-cols-5">
<form className="animate-in flex-1 flex flex-col w-full justify-center gap-2 text-foreground">
<label className="text-md" htmlFor="email">
Email
</label>
<input
className="rounded-md px-4 py-2 bg-inherit border mb-6"
name="email"
placeholder="[email protected]"
required
/>
<label className="text-md" htmlFor="password">
Password
</label>
<input
className="rounded-md px-4 py-2 bg-inherit border mb-6"
type="password"
name="password"
placeholder="••••••••"
required
/>
<SubmitButton
formAction={signIn}
className="bg-green-700 rounded-md px-4 py-2 text-foreground mb-2"
pendingText="Signing In..."
>
Sign In
</SubmitButton>
<SubmitButton
formAction={signUp}
className="border border-foreground/20 rounded-md px-4 py-2 text-foreground mb-2"
pendingText="Signing Up..."
>
Sign Up
</SubmitButton>
{searchParams?.message && (
<p className="mt-4 p-4 bg-foreground/10 text-foreground text-center">
{searchParams.message}
</p>
)}
</form>
</div>
</>
);
};
我认为这就是所有相关部分。当从 Supabase 成功接收用户对象时,我需要以某种方式更新其状态,而不仅仅是刷新页面时。我已经尝试了整个互联网上的一切。我只是不知道如何将所有这些组合在一起,使断开连接的组件可以在登录或注销后进行更新。我正在运行完全相同的堆栈,如果您发布 github 存储库,我会深入研究。您的代码对我来说看起来很好,我想到了有关 NextJS 缓存的问题。我有一个项目可以成功注销并更新我的身份验证组件。如果您愿意,我可以分享我的存储库,这是一个私人存储库。