سلام دوستان خسته نباشید
من پروژه ای با nextjs 13 پیاده سازی کردم و مشکلی که دارم هنگام ورود وقتی کاربر رو لاگین میکنم و ریدایرکت میشه به پنل خودش سریعا لاگین بودن کاربر توی پنل چک میشه و انگار کاربر لاگین نشده باشه مجدد برمیگرده صفحه لاگین و بعد چند لحظه کوتاه چون توی لاگین چک میشه اگر کاربر لاگین هست نیاز به بودنش توی این صفحه نیست ریدایرکت میشه به پنلش
من یه بخش توی پنل کاربری خودم دارم که لاگین بودن رو چک میکنه و یه بخش هم توی صفحه لاگین که چک میکنه کاربر لاگین هست یا نه
موقع خروج از سایت هم یه لحظه میره به صفحه لاگین و جون انگاری لاگینه سریع بر میگرده به پنل و بعد مدتی باز میره به لاگین و انگار تازه میفمه لاگ اوت شده
کد های من به شکل زیر هست و از async و await هم استفاده کردم
صفحه لاگین :
export default function Login () {
let [loading, setLoading] = useState<boolean>(false);
const router = useRouter()
const { user ,error , loading : authLogin } = useAuth();
if(authLogin) return <Preloader />
if(!error) {
// show error
router.push('/admin')
return <></>;
}
const submitHandler = async (values: any) => {
setLoading(true)
const res = await callApi().post('/auth/login', values)
if (res.status === 200 && res?.data?.status == 'success') {
storeLoginToken(res?.data?.token)
router.push('/admin')
}else{
setLoading(false)
}
}
بخش پنل کاربری :
export default function StaffLayout({
children,
}: {
children: React.ReactNode;
}) {
const router = useRouter();
const { user, error, loading } = useAuth();
if (loading) return <Preloader />;
if (error) {
// show error
router.push("/auth/login");
return <></>;
}
این هم هوک مربوط به چک کردن لاگین بودن کاربر
import useSWR from "swr";
import callApi from "../helpers/callApi";
const useAuth = () => {
const { data , error } = useSWR('auth_user' , () => {
return callApi().post('/auth/user')
})
return { user : data?.data?.res?.user , error , loading : !data && !error }
}
export default useAuth;
callApi تنظیمات اولیه axios هست و بازگشتی اون یک axiosInstance هست.
فقط این رو هم بگم که کد بالا یه خطایی بهم میداد که مربوط به route بود و برای همین اتفاق رفتو برگشتی بود
سرچ که کردم میگفت باید توی useEffect قرار بدی اون بخش کدت رو تا مشکلت حل بشه
این کار رو کردم و خطایی داشتم مبنی بر این که نمیتونی use auth hook رو توی useEffect استفاده کنی
مجدد که سرچ کردم میگفت بیا بزارش توی یک متغییر و توی useeffect چک کن ببین ایجاد شده یا نه بعد کارتو بکن
حالا کد من شد به شکل زیر خطایی ندارم ولی بازم مشکل سر جاشه و ریدایرکت بیخودی میکنه
بخش لاگین
export default function Login() {
let [loading, setLoading] = useState<boolean>(false);
const router = useRouter();
const auth = useAuth();
// const { user, error, loading: authLogin } = useAuth();
const checkAuth = async () => {
if (auth.loading) return <Preloader />;
if (!auth.error) {
// show error
router.push("/admin");
return <></>;
}
};
useEffect(() => {
if(auth && auth !=null)
checkAuth();
}, [auth]);
const submitHandler = async (values: any) => {
setLoading(true);
const res = await callApi().post("/auth/login", values);
if (res.status === 200 && res?.data?.status == "success") {
await storeLoginToken(res?.data?.token);
router.push("/admin");
} else {
setLoading(false);
}
};
بخش کاربری
export default function StaffLayout({
children,
}: {
children: React.ReactNode;
}) {
const router = useRouter();
const auth = useAuth();
const checkAuth = async () => {
if (auth.loading) return <Preloader />;
if (auth.error) {
// show error
router.push("/auth/login");
return <></>;
}
};
useEffect(() => {
if(auth && auth !== null)
checkAuth();
}, [auth]);
متد لاگ اوت هم که توی ساید بار منه
const logout = async () => {
await callApi().post("/auth/logout");
router.push("/auth/login");
};
@javadkarimii @mahdi.nazari
با تشکر از زحمات و وقتی که دوستان عزیز گزاشتند
من مشکل رو در دوره ی مربوطه خدمت مهندس موسوی اعلام کردم چون توی پروژه اصلی دوره هم این باگ وجود داشت
ایشون یه جلسه حل مشکل امروز ضبط کردن و مشکل رو توضیح و رفع کردن
با سپاس فراوان از مهندس موسوی عزیز بابت توضیح و وقتی که گزاشتن @hesammousavi
مشکل متاسفانه از مکانیزم کد نبود و از پکیج swr بود و یه موضوع cache کردن اطلاعت رو داره
برای حل این موضوع توی هوک مربوطه باید از mutate استفاده بشه به شکل زیر
import useSWR from "swr";
import callApi from "../helpers/callApi";
const useAuth = () => {
const { data , error , mutate } = useSWR('auth_user' , () => {
return callApi().post('/auth/user')
},)
return { user : data?.data , error , loading : !data && !error , mutate }
}
export default useAuth;
و بعد اون توی لاگین و لاگ اوت قبل از ریدایرکت کردن باید اون تابع mutate رو اجرا کرد
.......
const auth = useAuth();
// const { user, error, loading: authLogin } = useAuth();
useEffect(() => {
const checkAuth = async () => {
if (auth.loading) return <Preloader />;
if (!auth.error) {
// show error
router.push("/admin");
return <></>;
}
};
checkAuth();
}, [auth]);
... ...
...
...
const submitHandler = async (values: any) => {
setLoading(true);
const res = await callApi().post("/auth/login", values);
if (res.status === 200 && res?.data?.status == "success") {
await storeLoginToken(res?.data?.token);
await auth.mutate();
router.push("/admin");
} else {
setLoading(false);
}
};
همین
مشکل حل شد
مدل توابع غیر همزمان رو اینجوری کن
useEffect(() => {
const fetchData = async () => {
// Your async code here...
};
fetchData();
}, []);
یا اگر بیرون useEffect تعریف کردی باید از useCalback استفاده کنی
const fetchData = useCallback(async () => {
// Your async code here...
}, [/* dependencies of fetchData */]);
سلام عزیز مرسی از پاسختون @javadkarimii
اگر به شکل زیر منظورتون هست انجام دادم باز هم یک عمل رفتو برگشت سریع به پنل داریم و بعد یک ثانیه مثلا تشخیص میده لاگینه و بعد منتقل میشه به پنل
useEffect(() => {
const checkAuth = async () => {
if (auth.loading) return <Preloader />;
if (!auth.error) {
// show error
router.push("/admin");
return <></>;
}
};
if(auth && auth !=null){
checkAuth();
}
}, [auth]);
من یه چیزی یادم رفت بگم اینه که ورود من استفاده از کوکی هست و اونم به شکل httpOnly که از طریق header ارسال میشه به api server
متد لاگین من به شکل زیر هست
const storeLoginToken = async (token : string , days : number = 10) => {
await fetch('/api/login' , {
method : 'POST',
headers : {
'Content-Type' : 'application/json'
},
body : JSON.stringify({ token })
})
}
و روت مربوط به ذخیره کوکی هم به شکل زیر
import { NextResponse } from 'next/server'
import { cookies } from "next/headers";
export async function POST(req: Request) {
const { token } = await req.json()
cookies().set({
name: "sep_login_token",
value: token??'',
httpOnly: true,
maxAge: 60 * 60 * 24,
sameSite: "lax",
path: "/",
domain : process?.env?.NEXT_DOMAIN
// secure :
});
return NextResponse.json({ status: "success" })
}
مهندس موسوی توی دوره ی پروژه محور هم همچین روالی رو طی کردن
سابمیت لاگین به شکل زیر بوده
try {
const res = await callApi().post('/auth/login/verify-phone' , values)
if(res.status === 200) {
// clear phon verify token from redux
storeLoginToken(res.data?.user?.token);
await Router.push('/panel');
props.clearToken();
}
}
متد storelogin
import Cookies from 'universal-cookie';
const storeLoginToken = async (token : string , days : number = 10) => {
await fetch('/api/login' , {
method : 'POST',
headers : {
'Content-Type' : 'application/json'
},
body : JSON.stringify({ token })
})
}
const removeLoginToken = () => {
}
export { storeLoginToken , removeLoginToken };
روت مربوطه
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
import cookie from "cookie";
type Data = {
name: string
}
interface ExtendedNextApiRequest extends NextApiRequest {
body : {
token : string
}
}
export default function handler(
req: ExtendedNextApiRequest,
res: NextApiResponse<Data>
) {
res.setHeader(
"Set-Cookie",
cookie.serialize("shopy_token" , req.body?.token ,{
httpOnly : true,
maxAge : 60 * 60 * 24,
sameSite : "lax",
path: "/"
// domain : '.'
// secure :
})
)
res.status(200).json({ name: 'John Doe' })
}
و بخش پنلشون
const AdminPanelLayout = ({ children } : Props) => {
const router = useRouter();
const { user ,error , loading } = useAuth();
if(loading) return <h1>Loading ...</h1>
if(error) {
// show error
router.push('/auth/login')
return <></>;
}
if(! user?.is_admin ) {
router.push('/')
return <></>;
}
return (
<div className="w-full text-2xl">
{children}
</div>
)
}
export default AdminPanelLayout;
تفاوت منطقی خاصی نداره کدم با کد دوره آموزشی نمیدونم چرا این رفتار رو داره انگار کوکی رو لحظه اول ست نکرده باشه نتونه لاگین رو چک کنه مجبور میشه بندازه از پنل بیرون منو .
@javadkarimii
من الان کد مهندس موسوی رو هم چک کردم اون هم میره بعد لاگین کردن سریع منتقل میشه به صفحه لاگین (چون توی اعتبار سنجی دو مرحله ای هستیم اونجا و ثبت کد یکبار مصرف صفحه ی دیگریست ) بعدش میره یه صفحه پنل
خیلی سریع بود متوجهش نمیشدم تا بخش network مرورگر رو 3g slow کردم که بهم نشون دادش
مشکل از چیه و چطور جلوش رو بگیرم :((
این دوتا گزینه را فالس کن
const { data, error } = useSWR('auth_user', () => {
return callApi().post('/auth/user');
}, {
revalidateOnFocus: false,
revalidateOnReconnect: false
});
@javadkarimii
انجام دادم نشد
این موضوع که برا اینه وقتی از یه تب بریم به تب دیگه چک نکنه لاگین بودن رو
من لحظه ی اوله ریدایرکت شدن به پنل انگار کوکی رو نمیشناسه و من لاگین نیستم
لاگ گرفتم دقیقا خطا میده ورود رو منو میندازه بیرون بعد که میره توی لاگین باز چک میکنه میبینه لاگینم بعد میفرسته به پنل
یه کاری کردم rout.push رو بعد لاگین کردن برداشتم
وقتی لاگین میکنم بعد 10 ثانیه میفرسته منو به پنل و مشکلی نداره
یعنی 10 ثانیه طول میشکه که تشخیص بده من لاگینم اون متد چک کردنم توی لاگین که توی useEffect قرار دادم که اگر لاگین بود برو به پنل
من اول فکر میکردم که کوکی یه روت نیاز داره الکی تغییر کنه که شناسایی بشه برا همین در بار اول پیداش نمیکنه
با این تست دیدم نه مشکل از اونم نیست
اخه ورود async هم هست و await هم داره جای نیازش
چرا در همون لحظه تشخیص نمیده لاگینم
موقع خروج هم همینه داستان
تا ریدایرکت میشه بعد خروج فک میکنه هنوز لاگینم و دوباره برم میگردونه به پنل باز میبینه لاگ اوته میندازه بیرون
داستانی شده
دوستان من پروژه خودم رو همین بخش رو به صورت تنها مجدد ایجاد کردم و بارگزاری کردم برای دانلود
ممنون میشم یه تستی بکنید ببینید مشکل پیدا میشه یا نه
سپاس
دانلود فایل پروژه
سلام دوست من 🖐
امیدوارم حالت خوب و عالی باشی✨
مشکلی که توضیح دادی به نظر میرسه که به دلیل نداشتن یک مکانیزم مناسب برای مدیریت وضعیت لاگین کاربر در سایت، مشکل ایجاد شده . برای حل این مشکل، میتونی از متغیرها و متدهای مربوط به وضعیت لاگین بهتری استفاده کنی و همچنین بهترین روش برای اجرای عملیات بعد از لاگین یا لاگآوت، استفاده از یک متغیر گلوبال یا Redux و مدیریت وضعیت لاگین در آن باشه .
در اینجا چند نکته را به شما توصیه میکنم:
مدیریت وضعیت لاگین با Redux:
استفاده از Middleware برای حفاظت از مسیرها:
استفاده از Redux Thunk:
متدهای لاگآوت و لاگین مناسب:
استفاده از متغیرها بهجای کامپوننتهای تابعی:
استفاده از useEffect
مناسب:
useEffect
ایجاد شده، مطمئن شوید که این تابع در جای مناسب صدا زده میشود و تغییرات وضعیت لاگین را به درستی بررسی میکننبا اعمال این تغییرات، مشکل احتمالی که در ریدایرکتهای نادرست پیش میآید، باید حل بشه. همچنین اطمینان حاصل کنید که متغیرها و وضعیتها به درستی در سراسر پروژه مدیریت میشوند تا از تداخلها جلوگیری بشن
مشکلی بعدی که گفتی به نظر میرسه که ممکنه به دلیل تاخیر در اعمال کوکی یا انجام دیرینه درخواستها برای تغییر وضعیت لاگین کاربر باشد. از آنجایی که شما از کوکی با httpOnly
برای نگهداری توکن لاگین استفاده میکنی، کوکی به طور پیشفرض توسط مرورگر تنظیم میشود و ممکن است نیاز به زمانی برای تنظیم شدن داشته باشع.
برای حل این مشکل، میتوانید از یک نمودار حالت لاگین استفاده کنی که به طور مداوم وضعیت لاگین کاربر را بررسی میکند و به صورت مکرر درخواست به سرور میفرسته تا وضعیت لاگین کاربر را از سرور به درستی بهروز کنه.
به عنوان مثال، شما میتوانید از setInterval
در یک تابع checkLoginStatus
برای انجام این کار استفاده کنید. این تابع به انتظار تنظیم شدن کوکی و وضعیت لاگین کاربر مینشیند و در صورت تشخیص لاگین کاربر، به صفحه مورد نظر منتقل میشه
const checkLoginStatus = async () => {
const res = await callApi().post('/auth/user');
if (res.status === 200 && res?.data?.status === 'success') {
// کاربر لاگین شده است، به صفحه پنل منتقل شود
router.push('/admin');
}
};
// در useEffect، تابع checkLoginStatus را به صورت مکرر فراخوانی کنید
useEffect(() => {
const intervalId = setInterval(() => {
checkLoginStatus();
}, 1000); // هر ثانیه یک بار اجرا شود
return () => {
// پاکسازی تایمآوت در هنگام عدم استفاده از این useEffect
clearInterval(intervalId);
};
}, []);
با این تغییر، تابع checkLoginStatus
به صورت مکرر و بدون نیاز به رفتوبازگشت سریع به پنل وضعیت لاگین کاربر را بررسی میکند و در صورت تشخیص لاگین کاربر به صفحه مورد نظر او منتقل میشود. این روش ممکن است به تاخیر انداختن تنظیم کوکی و تغییر وضعیت کمک کنه
امیدوارم پاسخم بهت کمک کرده باشه ❤️
موفق و پیروز باشی 🤘🌹
این که فرستادی بررسی کردم
کدت از لحاظ منطقی مشکلی نداره
اولا که از کانتکس ها یا ریداکس استفاده کن اگر نمیخاید از متغیر های سراسری رو توی کوکی یا سشن استوریج استفاده کنید
شما از کوکی میگیری بعد از طریق auth توی کامپوننت ها استفاده میکنید ، کلا auth رو حذف کن و یکی از مواردی که بالا گفتم استفاده کن
@javadkarimii
@mahdi.nazari
سلام مجدد و سپاس بابت وقتی که میزارید
کلیتش که میگم طبق اموزش دوره پروژه محور مهندس موسوی هست
اما موضوعی که میگید
در رابطه با ریداکس من اگر موقع ورود و خروج بیام یه استیت رو تغییر بدم فقط خب نقش کوکی چی میشه ؟ اگر کوکی زمانش تموم شد کی باید چک کنم هین کار لاگین بودن رو
اصلا موضوع مهم تر اینه که توی api من از لاراول سنکتوم استفاده میکنم و قابلیتی که داره نشست های فعال رو میتونی ببینی و مثلا اگر پنج جا اکانتت باز هست یکیو بندازی بیرون مثل کاری که تلگرام یا واتس اپ انجام میده
یعنی مکانیزم کار ورود و خروج بهم میریزه اگر به حالت الان همش با خود کوکی و سرور نیام و چک نکنم
اگر بخوام auth حذف کنم عملا نباید از کوکی هم استفاده کنم جون بجز یه ورود که با پسورد چک میشه و توی ریداکس قرار داده میشه وضعیت من کوکی به کارم نمیاد دیگه
توی کوکی یا ... یه توکن ذخیره کن و توی هر ارسال که نیاز به لاگین بودن کاربر هست چک کن که معتبره یا نه ، هرجا نامعتبر بود از همون جا توکن از تو کوکی پاک کن
مگه توی لاراول میدلور لاگین بودن برای برخی روت هات نزاشتی ؟ همون جا وقتی ارور 401 بازگشت داد یعنی توکن نامعتبره و کوکی پاک میشه
توی کوکی یا ... یه توکن ذخیره کن و توی هر ارسال که نیاز به لاگین بودن کاربر هست چک کن که معتبره یا نه ، هرجا نامعتبر بود از همون جا توکن از تو کوکی پاک کن
خب من کوکی ای دارم فرض کن موقع ورود به سایت صفحه پنل کاربریم یه نمودار میخواد لود کنه
در اولین ورود کوکی ست نشده تشخیص میده و باز هم منو میندازه بیرون فک میکنه لاگین نیستم ، اگر بیرونم نرم چون ریداکس میکه تو لاگینی من توی کنسول ارور میخورم برای اولین بار همیشه چون در لحظه اول کوکی رو نتونستم ارسال کنم واسه سرور که داده ها رو بگیرم و درخواست متجدد داده ای رو باید هندل کنم که در اولین ورود برای دریافت داده ها اگر خطا خورد من لاگینم ولی بیرونم نمیرم داده ندارم اما باید بعد نیم ثانیه یا هرجی دوباره درخواست بدم به سرور
بالاخره همه ی روت های پنل توی api توی میدل ور سنکتوم هست
وقتی با توکن ذخیره شده در کوکی وارد پنل کاربری میشی ، پس یعنی توکن توی کوکی ست شده و تا آخر نرم افزار این توکن کارمیده
وقتی با توکن ذخیره شده در کوکی وارد پنل کاربری میشی ، پس یعنی توکن توی کوکی ست شده و تا آخر نرم افزار این توکن کارمیده
من یکم گیج شدم داداش منظورتون رو ، مشکل اصلی من ورود با توکن ذخیره شده توی کوکیه
کلا از اول ایراد این بود که کوکی ست میشه شناخته نمیشه و نمیفهمه کوکی هست و منو میندازه بیرون که راه حل مثلا این بود که از ریداکس استفاده کنیم که من گفتم اون اگر کوکی پاک کنیم داستان میشه و یا باز هم توی کنسول خطا رو مشاهده میکنیم چون api میگه لاگین نیستی و من با ریداکس اومدم تو و کوکی در لحظه اول شناسایی نمیشه
کلا مشکل کوکی بود از اول
@javadkarimii @mahdi.nazari
با تشکر از زحمات و وقتی که دوستان عزیز گزاشتند
من مشکل رو در دوره ی مربوطه خدمت مهندس موسوی اعلام کردم چون توی پروژه اصلی دوره هم این باگ وجود داشت
ایشون یه جلسه حل مشکل امروز ضبط کردن و مشکل رو توضیح و رفع کردن
با سپاس فراوان از مهندس موسوی عزیز بابت توضیح و وقتی که گزاشتن @hesammousavi
مشکل متاسفانه از مکانیزم کد نبود و از پکیج swr بود و یه موضوع cache کردن اطلاعت رو داره
برای حل این موضوع توی هوک مربوطه باید از mutate استفاده بشه به شکل زیر
import useSWR from "swr";
import callApi from "../helpers/callApi";
const useAuth = () => {
const { data , error , mutate } = useSWR('auth_user' , () => {
return callApi().post('/auth/user')
},)
return { user : data?.data , error , loading : !data && !error , mutate }
}
export default useAuth;
و بعد اون توی لاگین و لاگ اوت قبل از ریدایرکت کردن باید اون تابع mutate رو اجرا کرد
.......
const auth = useAuth();
// const { user, error, loading: authLogin } = useAuth();
useEffect(() => {
const checkAuth = async () => {
if (auth.loading) return <Preloader />;
if (!auth.error) {
// show error
router.push("/admin");
return <></>;
}
};
checkAuth();
}, [auth]);
... ...
...
...
const submitHandler = async (values: any) => {
setLoading(true);
const res = await callApi().post("/auth/login", values);
if (res.status === 200 && res?.data?.status == "success") {
await storeLoginToken(res?.data?.token);
await auth.mutate();
router.push("/admin");
} else {
setLoading(false);
}
};
همین
مشکل حل شد
سلام.
با swr کار نکردم اما هروقت از swr, react query یا rtk query استفاده میکنید سیستم caching رو دارید. اصلا دلیل استفاده از این سه تا برای remote state ها همینه.
پیشنهاد میکنم react query رو هم یاد بگیرید. چون بسیار جامعه بزرگی داره و سریع هم به جواب میرسید
آیا مایل به ارسال نوتیفیکیشن و اخبار از طرف راکت هستید ؟