Next.js 应用程序目录 - 完成异步功能后更新客户端组件。组件仅在页面刷新时更新

问题描述 投票:0回答:1
使用 Next.js(14.1.0,应用程序路由器,无 SRC 文件夹)。我需要一个客户端组件,该组件本身位于全局存在的菜单中,以便在用户登录时以某种方式进行更新,但是只有在刷新浏览器时它才会这样做。我尝试了状态、上下文和 useEffect 的各种实现,但均无济于事。我不确定如何在不发布大量代码片段的情况下解释这一点,所以请原谅本文的长度。这是详细信息。

我创建了一个样板,其中有一个指向相应路线的简单导航菜单。该菜单是在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 缓存的问题。我有一个项目可以成功注销并更新我的身份验证组件。如果您愿意,我可以分享我的存储库,这是一个私人存储库。
reactjs next.js app-router
1个回答
0
投票

© www.soinside.com 2019 - 2024. All rights reserved.