اذهب إلى المحتوى

السؤال

نشر

السلام عليكم.

قمت ببناء مكون React للتحقق مما إذا كان المستخدم قد فعل حسابه. حسب نتيجة العملية، أريد عرض أحد المكونات التالية:

1.نجاح التحقق:

          <>
            <AiOutlineCheckCircle className="text-teal-600 dark:text-teal-400 text-6xl mx-auto animate-bounce mb-4" />
            <h1 className="text-xl md:text-2xl font-semibold text-gray-800 dark:text-white mb-2">
              {/* {data?.message} */}
              Valid
            </h1>
            <Link
              to="/login"
              className="inline-block bg-teal-600 hover:bg-teal-700 text-white font-medium py-2 px-6 rounded-full transition-colors duration-300"
            >
              Login
            </Link>
          </>

2.فشل التحقق

          <>
            <AiOutlineWarning className="text-red-500 dark:text-red-400 text-6xl mx-auto animate-pulse mb-4" />
            <h1 className="text-xl font-semibold text-gray-800 dark:text-white">
              error
            </h1>
            <p className="text-sm text-gray-600 dark:text-gray-300 mt-2">
              Please check your verification link or request a new one.
            </p>
          </>

المشكلة:
في الوقت الحالي، عند فتح صفحة التحقق، يظهر أولًا مكون النجاح لبضع ثوانٍ، ثم يتحول فجأة إلى مكون الفشل، رغم أنّ عملية التحقق في قاعدة البيانات تمّت بنجاح.

للمزيد من التوضيح تفضلوا:

كود الواجهة الخلفية (Node.js/Express):

export const verifyAccount = asyncHandler(async(req, res) => {
  const { userId, token } = req.params
  // Check if user exist
  const user = await User.findById(userId)
  if (!user) {
    return res.status(400).json({ message: "Invalid Verification Link" })
  }

  const verificationToken = await VerificationToken.findOne({
    userId: user._id,
    token: token,
  })
  if (!verificationToken) {
    return res.status(400).json({ message: "Invalid Link" });
  }
  user.isAccountVerified = true
  await user.save()

  await VerificationToken.deleteOne({ _id: verificationToken._id });

  res.status(200).json({ message: "Your Account is Verified!" })
})

علما أنه في حتى حاة ظهور المكون الخاص بفشل العملية في الواجهة الأمامية فإنه تم تفعيل 

  user.isAccountVerified = true
  await user.save()

  await VerificationToken.deleteOne({ _id: verificationToken._id });

أما بالنسبة للواجهة الأمامية

  • api
export const verifyEmailApi = async ({ userId, token }) => {
  try {
    const res = await axios.get(summaryApi.auth.verify(userId, token))
    console.log("api", res.data)
    return res.data
  } catch (error) {
    console.error(error);
    throw error;
  }
}
  • queries
export const useVerifyEmail = () => {
  return useMutation({
    mutationFn: verifyEmailApi,
    onSuccess: (data) => {
      console.log(data)
    }
  });
}
  • VerifEmailComponent
const VerifyEmail = () => {
  const { userId, token } = useParams();
  const { mutate, data, isError, isPending } = useVerifyEmail();
  console.log(" data", data) // undefined

  const verify = () => {
    if (userId && token) {
      console.log("👉 sending mutation with", { userId, token, data }); // data: undefined, token: defined, userId: defined
      mutate({ userId, token });
    }
  }

  useEffect(() => {
    verify()
  }, [userId, token, mutate]);

  if (isPending) {
    return <Spinner/>
  }
  
  return (
    <section className="flex items-center justify-center min-h-[calc(100vh-2rem)] px-4">
      // الكود الذي قمت بإدراجه سلفا
    </section>
  );
};

سؤالي:
لماذا يظهر مكون النجاح أولًا لثوانٍ ثم يتحول إلى مكون الفشل، رغم أنّ التحقق في قاعدة البيانات تم بنجاح؟ وكيف يمكنني تعديل الكود الأمامي بحيث يظهر المكون الصحيح مباشرة بناءً على نتيجة العملية دون هذا التبديل الغير مرغوب فيه؟

شكرا جزيلا.

Recommended Posts

  • 0
نشر
بتاريخ 2 دقائق مضت قال عماد شيخ العشرة:

هل من الممكن ارسال منطق التحكم بظهور المكون الخاص بالنجاح او الفشل فأنا لا اراه هنا قد يكون الخلل منه.

تقصد هذا المكون

import React, { useEffect, useState } from "react";
import { Link, useParams } from "react-router-dom";
import { useVerifyEmail } from "../lib/queries/auth.queries";
import { AiOutlineCheckCircle, AiOutlineWarning } from "react-icons/ai";
import Spinner from "../components/loaders/Spinner";

const VerifyEmail = () => {
  const { userId, token } = useParams();
  const { mutate, data, isError, isPending } = useVerifyEmail();
  console.log(" data", data) // undefined

  const verify = () => {
    if (userId && token) {
      console.log("👉 sending mutation with", { userId, token, data }); // data: undefined, token: defined, userId: defined
      mutate({ userId, token });
    }
  }

  useEffect(() => {
    verify()
  }, [userId, token, mutate]);

  if (isPending) {
    return <Spinner/>
  }
  
  return (
    <section className="flex items-center justify-center min-h-[calc(100vh-2rem)] px-4">
      <div className="w-full max-w-md bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-2xl shadow-md p-6 text-center transition-all duration-300">
        {isError ? (
          <>
            <AiOutlineWarning className="text-red-500 dark:text-red-400 text-6xl mx-auto animate-pulse mb-4" />
            <h1 className="text-xl font-semibold text-gray-800 dark:text-white">
              error
            </h1>
            <p className="text-sm text-gray-600 dark:text-gray-300 mt-2">
              Please check your verification link or request a new one.
            </p>
          </>
        ) : (
          <>
            <AiOutlineCheckCircle className="text-teal-600 dark:text-teal-400 text-6xl mx-auto animate-bounce mb-4" />
            <h1 className="text-xl md:text-2xl font-semibold text-gray-800 dark:text-white mb-2">
              {/* {data?.message} */}
              Valid
            </h1>
            <Link
              to="/login"
              className="inline-block bg-teal-600 hover:bg-teal-700 text-white font-medium py-2 px-6 rounded-full transition-colors duration-300"
            >
              Login
            </Link>
          </>
        )}
      </div>
    </section>
  );
};

export default VerifyEmail;

 

  • 0
نشر
بتاريخ 8 دقائق مضت قال عماد شيخ العشرة:

جرب هذا الحل وهو تأكد ان الdata موجودة ولا لا وعلى اساسها نختار النجاح او غيره:

useEffect(() => {
  verify();
}, [userId, token]); 

هل بامكانك تبديل خطاف useEffect بالتالي واخباري بالنتائج.

نفس المشكل أخي

  • 0
نشر
return (
  <section className="flex items-center justify-center min-h-[calc(100vh-2rem)] px-4">
    <div className="w-full max-w-md bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-2xl shadow-md p-6 text-center transition-all duration-300">
      {isError ? (
        <>
          <AiOutlineWarning className="text-red-500 text-6xl mx-auto animate-pulse mb-4" />
          <h1 className="text-xl font-semibold">Error</h1>
          <p className="text-sm mt-2">Invalid or expired link.</p>
        </>
      ) : data ? (
        <>
          <AiOutlineCheckCircle className="text-teal-600 text-6xl mx-auto animate-bounce mb-4" />
          <h1 className="text-xl font-semibold">{data.message}</h1>
          <Link
            to="/login"
            className="inline-block bg-teal-600 hover:bg-teal-700 text-white font-medium py-2 px-6 rounded-full transition-colors duration-300"
          >
            Login
          </Link>
        </>
      ) : null}
    </div>
  </section>
);

فلنتأكد من المعلومات هل هي موجودة ام لا باستخدام data ? وهل يمكنك ارفاق ماذا يحصل في ال console ايضا؟

  • 0
نشر
بتاريخ 3 دقائق مضت قال عماد شيخ العشرة:
return (
  <section className="flex items-center justify-center min-h-[calc(100vh-2rem)] px-4">
    <div className="w-full max-w-md bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-2xl shadow-md p-6 text-center transition-all duration-300">
      {isError ? (
        <>
          <AiOutlineWarning className="text-red-500 text-6xl mx-auto animate-pulse mb-4" />
          <h1 className="text-xl font-semibold">Error</h1>
          <p className="text-sm mt-2">Invalid or expired link.</p>
        </>
      ) : data ? (
        <>
          <AiOutlineCheckCircle className="text-teal-600 text-6xl mx-auto animate-bounce mb-4" />
          <h1 className="text-xl font-semibold">{data.message}</h1>
          <Link
            to="/login"
            className="inline-block bg-teal-600 hover:bg-teal-700 text-white font-medium py-2 px-6 rounded-full transition-colors duration-300"
          >
            Login
          </Link>
        </>
      ) : null}
    </div>
  </section>
);

فلنتأكد من المعلومات هل هي موجودة ام لا باستخدام data ? وهل يمكنك ارفاق ماذا يحصل في ال console ايضا؟

data دائما undefined

تفضل صورة تبين نتائج console

Capture.thumb.JPG.5f12e327567639b62d1d26206be28a72.JPG

  • 0
نشر
بتاريخ 1 دقيقة مضت قال عماد شيخ العشرة:

اعتقد ان سبب المشكلة هو التغير في ال depencincies الخاصة ب useEffect جرب التالي:

useEffect(() => {
  if (userId && token) {
    mutate({ userId, token });
  }
}, []);

بدل استخدام دالة verify وغالبا سيعمل.

نفس الشيء أخي، نفس الخطأ في الـUI

  • 0
نشر
بتاريخ 22 ساعة قال محمود سعداوي2:

هذا المشروع كاملا فقط قم بـ

cd frontend
npm run frontend

**********************
cd backend
npm run backend

hsoub.zip 51.41 MB · 2 تنزيلات

شكرا جزيلا

إذا نظرت إلى ال console ستجد أنه يتم طباعة وتنفيذ ال API الخاص بالتحقق مرتين وهذا هو سبب المشكلة الرئيسية .

ففي أول مرة يتم تنفيذ المكون يتم إرسال التحقق مما يسبب نجاح العملية ولكن مع الإرسال الثاني يظهر الخطأ لأن بالفعل المستخدم تم التحقق من حسابه وتم حذف رمز ال token من قاعدة البيانات.

وهذه المشكلة بسبب انك تعمل في وضع React الصارم (React.StrictMode) أثناء التطوير.

حيث في وضع التطوير (Development Mode) يقوم React.StrictMode بعرض كل مكون مرتين (mount -> unmount -> mount) بشكل متعمد والهدف من ذلك هو مساعدتك في اكتشاف الأخطاء والآثار الجانبية (Side Effects) غير المرغوب فيها في الكود الخاص بك.

ولذلك يوجد عدة حلول لديك :

أولا  يمكنك حذف الوضع الصارم من ملف frontend\src\main.jsx .

ثانيا يمكنك حاليا عدم حذف ال token من قاعدة البيانات وذلك في وضع التطوير أما في وضع النشر production يمكنك حذفه .

ثالثا يمكنك إستخدام useRef هكذا :

import React, { useEffect, useRef } from "react";
import { Link, useParams } from "react-router-dom";
import { useVerifyEmail } from "../lib/queries/auth.queries";
import { AiOutlineCheckCircle, AiOutlineWarning } from "react-icons/ai";
import Spinner from "../components/loaders/Spinner";

const VerifyEmail = () => {
  const { userId, token } = useParams();
  const { mutate, data, isError, isPending } = useVerifyEmail();
  const effectRan = useRef(false);

  useEffect(() => {
    if (effectRan.current === false) {
      if (userId && token) {
        mutate({ userId, token });
      }
    }    

    return () => {
      effectRan.current = true;
    };
  }, []);
  if (!effectRan.current) {
    return <Spinner />;
  }
  
  return (
    <section className="flex items-center justify-center min-h-[calc(100vh-2rem)] px-4">
      <div className="w-full max-w-md bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-2xl shadow-md p-6 text-center transition-all duration-300">
        {isError ? (
          <>
            <AiOutlineWarning className="text-red-500 dark:text-red-400 text-6xl mx-auto animate-pulse mb-4" />
            <h1 className="text-xl font-semibold text-gray-800 dark:text-white">
              Error Verifying Email
            </h1>
            <p className="text-sm text-gray-600 dark:text-gray-300 mt-2">
              Please check your verification link or request a new one.
            </p>
          </>
        ) : (
          <>
            <AiOutlineCheckCircle className="text-teal-600 dark:text-teal-400 text-6xl mx-auto animate-bounce mb-4" />
            <h1 className="text-xl md:text-2xl font-semibold text-gray-800 dark:text-white mb-2">
              {data?.message || "Email Verified Successfully!"}
            </h1>
            <Link
              to="/login"
              className="inline-block bg-teal-600 hover:bg-teal-700 text-white font-medium py-2 px-6 rounded-full transition-colors duration-300"
            >
              Login
            </Link>
          </>
        )}
      </div>
    </section>
  );
};

export default VerifyEmail;

 

  • 0
نشر
بتاريخ 33 دقائق مضت قال محمد_عاطف:

إذا نظرت إلى ال console ستجد أنه يتم طباعة وتنفيذ ال API الخاص بالتحقق مرتين وهذا هو سبب المشكلة الرئيسية .

ففي أول مرة يتم تنفيذ المكون يتم إرسال التحقق مما يسبب نجاح العملية ولكن مع الإرسال الثاني يظهر الخطأ لأن بالفعل المستخدم تم التحقق من حسابه وتم حذف رمز ال token من قاعدة البيانات.

وهذه المشكلة بسبب انك تعمل في وضع React الصارم (React.StrictMode) أثناء التطوير.

حيث في وضع التطوير (Development Mode) يقوم React.StrictMode بعرض كل مكون مرتين (mount -> unmount -> mount) بشكل متعمد والهدف من ذلك هو مساعدتك في اكتشاف الأخطاء والآثار الجانبية (Side Effects) غير المرغوب فيها في الكود الخاص بك.

ولذلك يوجد عدة حلول لديك :

أولا  يمكنك حذف الوضع الصارم من ملف frontend\src\main.jsx .

ثانيا يمكنك حاليا عدم حذف ال token من قاعدة البيانات وذلك في وضع التطوير أما في وضع النشر production يمكنك حذفه .

ثالثا يمكنك إستخدام useRef هكذا :

import React, { useEffect, useRef } from "react";
import { Link, useParams } from "react-router-dom";
import { useVerifyEmail } from "../lib/queries/auth.queries";
import { AiOutlineCheckCircle, AiOutlineWarning } from "react-icons/ai";
import Spinner from "../components/loaders/Spinner";

const VerifyEmail = () => {
  const { userId, token } = useParams();
  const { mutate, data, isError, isPending } = useVerifyEmail();
  const effectRan = useRef(false);

  useEffect(() => {
    if (effectRan.current === false) {
      if (userId && token) {
        mutate({ userId, token });
      }
    }    

    return () => {
      effectRan.current = true;
    };
  }, []);
  if (!effectRan.current) {
    return <Spinner />;
  }
  
  return (
    <section className="flex items-center justify-center min-h-[calc(100vh-2rem)] px-4">
      <div className="w-full max-w-md bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-2xl shadow-md p-6 text-center transition-all duration-300">
        {isError ? (
          <>
            <AiOutlineWarning className="text-red-500 dark:text-red-400 text-6xl mx-auto animate-pulse mb-4" />
            <h1 className="text-xl font-semibold text-gray-800 dark:text-white">
              Error Verifying Email
            </h1>
            <p className="text-sm text-gray-600 dark:text-gray-300 mt-2">
              Please check your verification link or request a new one.
            </p>
          </>
        ) : (
          <>
            <AiOutlineCheckCircle className="text-teal-600 dark:text-teal-400 text-6xl mx-auto animate-bounce mb-4" />
            <h1 className="text-xl md:text-2xl font-semibold text-gray-800 dark:text-white mb-2">
              {data?.message || "Email Verified Successfully!"}
            </h1>
            <Link
              to="/login"
              className="inline-block bg-teal-600 hover:bg-teal-700 text-white font-medium py-2 px-6 rounded-full transition-colors duration-300"
            >
              Login
            </Link>
          </>
        )}
      </div>
    </section>
  );
};

export default VerifyEmail;

 

شكرا لك @محمد_عاطف

في الواقع أرى أن الحل الأنسب هو تعديل الواجهة الخلفية ليصبح بهذه الطريقة

export const verifyAccount = asyncHandler(async(req, res) => {
  const { userId, token } = req.params
  // Check if user exist
  const user = await User.findById(userId)
  if (!user) {
    return res.status(400).json({ message: "Invalid Verification Link" })
  }

  const verificationToken = await VerificationToken.findOne({
    userId: user._id,
    token: token,
  })
  if (!verificationToken) {
    if (user.isAccountVerified) {
      return res.status(200).json({ message: "Your Account is Already Verified!" });
    }
    return res.status(400).json({ message: "Invalid Link" });
  }
  user.isAccountVerified = true
  await user.save()

  await VerificationToken.deleteMany({ userId: user._id });

  res.status(200).json({ message: "Your Account is Verified!" })
})

ففي حالتي أنا بالرغم user.isAccountVerified و !verificationToken فيظهر مكون الخطأ والسبب يعود لـ tanstack query مثلما تفضلتم بالشرح

لذلك إعتمدت على تغيير منطق الواجهة الخلفية والمحافظة على الواجهة الأمامية كما هي والسبب هو التوافق مع  TanStack Query والمحافظة على frontend بسيط يعتمد على TanStack Query (isLoading, isError, data) بالإضافة إلى ضمان تجربة متكاملة بين التطوير واإنتاج.

شكرا جزيلا مجددا

 

 

انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أجب على هذا السؤال...

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.

  • إعلانات

  • تابعنا على



×
×
  • أضف...