student Details

بدل أن يكون لديك فقط:

عرض الطلاب
تعديل
حذف
JSX

سيصبح لديك أيضًا:

عرض التفاصيل
JSX

🎯 الفكرة

في جدول الطلاب تضيف زر:

<Link
  to={`/students/${student.documentId}`}
  className="btn btn-info btn-sm"
>
  التفاصيل
</Link>
JSX

🚀 الراوت

داخل App.jsx

<Route
  path="/students/:id"
  element={<StudentDetails />}
/>
JSX

🎯 إنشاء الصفحة

pages/students/StudentDetails.jsx
JSX

🚀 الكود الأساسي

import { useParams } from "react-router-dom";
import { useSelector } from "react-redux";

const StudentDetails = () => {

  const { id } = useParams();

  const { students } = useSelector(
    (state) => state.students
  );

  const student = students.find(
    (student) => student.documentId === id
  );

  if (!student) {
    return (
      <div className="alert alert-danger">
        الطالب غير موجود
      </div>
    );
  }

  return (
    <div className="container-fluid">

      <div className="card">

        <div className="card-header">

          <h3 className="card-title">

            تفاصيل الطالب

          </h3>

        </div>

        <div className="card-body">

          <div className="row">

            <div className="col-md-6">

              <p>
                <strong>كود الطالب:</strong>
                {" "}
                {student.st_code}
              </p>

              <p>
                <strong>اسم الطالب:</strong>
                {" "}
                {student.name}
              </p>

              <p>
                <strong>العمر:</strong>
                {" "}
                {student.age}
              </p>

              <p>
                <strong>الهاتف:</strong>
                {" "}
                {student.phone}
              </p>

              <p>
                <strong>البريد الإلكتروني:</strong>
                {" "}
                {student.email}
              </p>

            </div>

            <div className="col-md-6">

              <p>
                <strong>العنوان:</strong>
                {" "}
                {student.address}
              </p>

              <p>
                <strong>زمرة الدم:</strong>
                {" "}
                {student.blood}
              </p>

              <p>
                <strong>اسم الأب:</strong>
                {" "}
                {student.father}
              </p>

              <p>
                <strong>اسم الأم:</strong>
                {" "}
                {student.mother}
              </p>

            </div>

          </div>

        </div>

      </div>

    </div>
  );

};

export default StudentDetails;
JSX

🎯 إظهار القسم والصف والمرحلة

بما أنك خزنت:

section
JSX

فقط

يمكنك الوصول لباقي العلاقات هكذا:

<p>
  <strong>القسم:</strong>
  {" "}
  {student.section?.name}
</p>

<p>
  <strong>الصف:</strong>
  {" "}
  {student.section?.classroom?.name}
</p>

<p>
  <strong>المرحلة:</strong>
  {" "}
  {student.section?.classroom?.grade?.name}
</p>
JSX

⚠️ مهم جداً

لكي تعمل الأسطر السابقة يجب أن يكون fetchStudents يحتوي على populate للعلاقات.

مثال في Strapi:

/students?populate=section.classroom.grade
JSX

أو

/students?populate=*
JSX

أثناء التطوير.


🚀 الشكل الاحترافي الذي أنصح به

بدل عرض البيانات كسطور عادية:

اعمل بطاقتين (Cards)

البطاقة الأولى

المعلومات الشخصية
JSX
  • الاسم
  • الكود
  • العمر
  • الهاتف
  • الإيميل

البطاقة الثانية

المعلومات الأكاديمية
JSX
  • المرحلة
  • الصف
  • القسم

البطاقة الثالثة

معلومات ولي الأمر
JSX
  • الأب
  • الأم
  • العنوان

🎯 خطوة أكثر احترافية

إذا كنت تنوي لاحقًا إضافة:

  • صورة الطالب
  • السجل الدراسي
  • الحضور والغياب
  • الأقساط

فأنصحك من الآن أن تجعل صفحة التفاصيل تعتمد على:

fetchSingleStudent(id)
JSX

وليس:

students.find(...)
JSX

لأن عند تحديث الصفحة (F5) ستختفي البيانات إذا لم تكن موجودة في Redux.

لذلك في الأنظمة الحقيقية غالبًا يكون لديك:

/student/abc123
JSX

ثم:

dispatch(fetchSingleStudent(id))
JSX

ويتم جلب الطالب مباشرة من الـ API.

وهذه هي الطريقة التي أوصي بها عندما ننتهي من CRUD الأساسي. 🚀

نعم اختفتالمعلومات عند التحديث كيف يمكن حلها اكتب بالتفصيل

ممتاز 🔥 وهذه من أهم المشاكل التي تواجه أي شخص يتعلم React + Redux لأول مرة.


🎯 لماذا تختفي البيانات عند F5؟

لنفترض أنك دخلت إلى:

/students/abc123
JSX

ثم صفحة التفاصيل تعمل هكذا:

const { students } = useSelector(
  (state) => state.students
);

const student = students.find(
  (student) => student.documentId === id
);
JSX

قبل التحديث

Redux يحتوي:

students = [
  { documentId: "abc123", name: "محمد" }
]
JSX

فيجد الطالب ويعرضه.


بعد التحديث F5

المتصفح يعيد تحميل التطبيق بالكامل.

Redux يعود إلى:

initialState = {
  students: []
}
JSX

فتصبح:

students = []
JSX

النتيجة

هذا السطر:

students.find(...)
JSX

يرجع:

undefined
JSX

فتختفي البيانات.


🎯 ما هو الحل الاحترافي؟

بدلاً من الاعتماد على:

students.find(...)
JSX

نجلب الطالب مباشرة من الـ API.


المعمارية الصحيحة

عند فتح:

/students/abc123
JSX

يجب أن يحدث:

1- قراءة id من الرابط
2- إرسال طلب API
3- جلب الطالب
4- عرض البيانات
JSX

🚀 إنشاء fetchSingleStudent

داخل studentsSlice


thunk

export const fetchSingleStudent = createAsyncThunk(

  "students/fetchSingleStudent",

  async (id, thunkAPI) => {

    try {

      const response = await axiosInstance.get(

        `/students/${id}?populate=section.classroom.grade`

      );

      return response.data.data;

    } catch (error) {

      return thunkAPI.rejectWithValue(
        error.response?.data || error.message
      );

    }

  }

);
JSX

إضافة State جديد

بدل:

const initialState = {

  students: [],

  loading: false,

  error: null

};
JSX

اجعل:

const initialState = {

  students: [],

  student: null,

  loading: false,

  error: null

};
JSX

addCase

.addCase(fetchSingleStudent.pending, (state) => {

    state.loading = true;

})

.addCase(fetchSingleStudent.fulfilled, (state, action) => {

    state.loading = false;

    state.student = action.payload;

})

.addCase(fetchSingleStudent.rejected, (state, action) => {

    state.loading = false;

    state.error = action.payload;

})
JSX

🎯 صفحة التفاصيل

الآن لا نستخدم:

find()
JSX

أبداً.


StudentDetails.jsx

const { id } = useParams();

const dispatch = useDispatch();

const {
  student,
  loading
} = useSelector(
  (state) => state.students
);
JSX

جلب البيانات

useEffect(() => {

  dispatch(fetchSingleStudent(id));

}, [dispatch, id]);
JSX

حالة التحميل

if (loading) {

  return <h3>جاري التحميل...</h3>;

}
JSX

إذا لم يوجد طالب

if (!student) {

  return (
    <div className="alert alert-danger">
      الطالب غير موجود
    </div>
  );

}
JSX

عرض البيانات

<h2>{student.name}</h2>

<p>{student.phone}</p>

<p>{student.email}</p>

<p>{student.section?.name}</p>

<p>{student.section?.classroom?.name}</p>

<p>{student.section?.classroom?.grade?.name}</p>
JSX

🎯 لماذا هذه الطريقة أفضل؟

لأنها تعمل في كل الحالات:

الحالةالنتيجة
الدخول من صفحة الطلاب
نسخ الرابط وفتحه مباشرة
F5
مشاركة الرابط مع شخص آخر

🔥 نفس الفكرة يجب تطبيقها على صفحة التعديل

بدلاً من:

const student = students.find(...)
JSX

استخدم:

fetchSingleStudent(id)
JSX

ثم:

reset(student)
JSX

عند وصول البيانات.


🎯 لماذا الشركات الكبيرة تفعل ذلك؟

لأن الرابط:

/students/abc123
JSX

يجب أن يكون مستقلاً بذاته.

أي أن الصفحة قادرة على العمل حتى لو:

Redux فارغ
JSX

أو:

تم تحديث الصفحة
JSX

وهذا ما يسمى:

Route Driven Data Fetching
JSX

وهو الأسلوب الاحترافي المستخدم في أغلب الأنظمة الحديثة. 🚀