The header decided between profile and login button only from the server-passed logged prop (read once via cookies() in StLayout), so a user who logged in client-side still saw ورود | ثبت نام until a hard reload. Seed isLogged from the server prop (correct first paint, no hydration mismatch) then sync it from the access_token cookie on mount and on every route change, so login/logout reflect immediately. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7.1 KiB
رفع نمایش «ورود | ثبتنام» در هدر با وجود لاگینبودن کاربر
پروژه
nobat724_front — سایت عمومی. این کار صرفاً frontend است؛ backend تغییر نمیکند.
زمینه
هدر سایت عمومی بسته به وضعیت لاگین، یا دکمهی «ورود | ثبت نام» را نشان میدهد یا آیکن پروفایل کاربر (ProfileUser). تصمیم بر اساس prop logged گرفته میشود که در components/layout/StLayout.js (Server Component) از روی کوکی access_token با cookies() خوانده و به هدر پاس داده میشود.
مشکل: کاربر بعد از لاگین موفق (کوکیهای access_token/refresh_token/uuid/userInfo با js-cookie ست میشوند)، هنوز در هدر «ورود | ثبت نام» میبیند. علت: logged فقط یکبار سمت سرور هنگام render محاسبه میشود و به ناوبریهای client-side (soft navigation) و کوکیهایی که سمت کلاینت ست شدهاند واکنش نشان نمیدهد؛ هدر تا یک hard-reload کامل وضعیت تازه را منعکس نمیکند و در بسیاری از مسیرها همان حالت اولیهی «لاگیننشده» باقی میماند.
مشکل / هدف
هدر باید وضعیت لاگین را بهصورت زنده سمت کلاینت هم تشخیص دهد: اگر کوکی access_token وجود دارد → پروفایل، وگرنه → دکمهی ورود. prop سرور (logged) بهعنوان مقدار اولیهی paint اول حفظ شود تا flash/hydration mismatch ندهد، ولی منبع نهایی تصمیم، کوکی سمت کلاینت باشد.
فایلهای مرتبط
| فایل | نقش |
|---|---|
components/layout/header/Content.js |
"use client"؛ بر اساس logged بین ProfileUser و دکمهی ورود سوییچ میکند — محل اصلی رفع |
components/layout/StLayout.js |
Server Component؛ logged={token} را از cookies().get("access_token") میدهد (مقدار اولیه) |
components/layout/index.js |
layout کلاینتیِ پنل/داشبورد؛ logged را بهصورت prop میگیرد |
components/layout/header/index.js |
فقط prop را پاس میدهد |
components/layout/header/profile/index.js |
ProfileUser — حالت لاگینشده |
utils/index.js |
removeToken() — خروج |
وضعیت فعلی (کد واقعی)
header/Content.js — تصمیم فقط بر اساس prop سرور
"use client";
// ...
function Content({ matchedCity, name, logged }) {
// ...
{logged ? (
<ProfileUser />
) : (
<Link href="/login">
<Button variant="outlined" color="primary">ورود | ثبت نام</Button>
</Link>
)}
}
StLayout.js — مقدار اولیه از کوکی سرور
const cookieStore = await cookies();
const token = cookieStore.get("access_token");
// ...
<Header matchedCity={matchedCity} name={name} logged={token} />
tokenیک شیء{ name, value }یاundefinedاست؛ پسloggedtruthy/undefined میشود. این فقط در render سرور محاسبه میشود.
کوکیها سمت کلاینت ست میشوند (SendReq.js)
Cookies.set("access_token", response.access_token, cookieOptions); // js-cookie, غیر httpOnly
وظایف
۱. تشخیص زندهی لاگین در header/Content.js
- یک state کلاینتی برای لاگینبودن اضافه کن که مقدار اولیهاش از prop
logged(سرور) میآید تا paint اول درست باشد، سپس درuseEffectبا کوکیaccess_tokenهمگام شود:
"use client";
import { useEffect, useState } from "react";
import Cookies from "js-cookie";
// ...
function Content({ matchedCity, name, logged }) {
const [isLogged, setIsLogged] = useState(Boolean(logged));
useEffect(() => {
setIsLogged(Boolean(Cookies.get("access_token")));
}, []);
// ...
{isLogged ? <ProfileUser /> : <Link href="/login">...دکمه ورود...</Link>}
}
Boolean(logged)مهم است:loggedممکن است شیء{name,value}یاundefinedباشد؛ تبدیل به boolean کن.- چون مقدار اولیه از سرور میآید، hydration mismatch رخ نمیدهد؛ بعد از mount با کوکی واقعی کلاینت تصحیح میشود.
۲. واکنش به تغییر مسیر (اختیاری ولی توصیهشده)
- اگر لازم بود وضعیت بعد از login/logout بدون hard-reload بهروز شود، میتوانی
usePathname()را به deps افزودن useEffect اضافه کنی تا با هر تغییر مسیر کوکی دوباره چک شود:
import { usePathname } from "next/navigation";
const pathname = usePathname();
useEffect(() => {
setIsLogged(Boolean(Cookies.get("access_token")));
}, [pathname]);
اگر فلوی فعلی login با
window.location.hrefریدایرکت میکند (hard reload)، paint اول سرور هم درست خواهد بود؛ این مرحله بیشتر برای ناوبریهای soft و logout است.
۳. سازگاری logout
ProfileUser/DetailProfileهنگام logoutremoveToken()را صدا میزنند که کوکیها را پاک میکند. مطمئن شو بعد از logout، هدر دوباره دکمهی ورود را نشان میدهد (با همان مکانیزم کوکیخوانی + ناوبری/ریلود فعلی). اگر logout فقط state را عوض میکند و ریدایرکت ندارد، بررسی کن هدر بهروز شود.
نکات مهم
- هیچ تغییری در backend لازم نیست. کوکی
access_tokenغیر httpOnly است و سمت کلاینت باjs-cookieقابلخواندن است. - Hydration: مقدار اولیهی state حتماً از prop
loggedسرور باشد، نه مستقیماً ازCookies.getدر زمان render؛ خواندن کوکی فقط داخلuseEffect(بعد از mount) تا SSR و کلاینت در paint اول یکی باشند. - multi-domain / RTL را خراب نکن؛ منطق
matchedCity/nameدستنخورده بماند. - این هدر هم در
StLayout(صفحات عمومی، logged از سرور) و هم درcomponents/layout/index.js(پنل/داشبورد، logged از prop) استفاده میشود؛ هر دو مسیر باید بعد از تغییر کار کنند (در هر دو، fallback به prop + همگامسازی با کوکی درست عمل میکند). - تست:
npm run build؛ سپس دستی: قبل از لاگین → دکمهی «ورود | ثبت نام»؛ بعد از لاگین با12345و بازگشت به صفحه → آیکن پروفایل؛ بعد از logout → دوباره دکمهی ورود. سپس commit با پیام توصیفی.