نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
REA

منهج React Js

دليل مرجعي شامل

كل اللغات
مدرسة البرمجة/منهج React Js/useState وحفظ مبدأ Immutability

useState وحفظ مبدأ Immutability

تحديث الكائنات في، ImmutabilityuseState في ريأكتاستخدام Spread Operator مع React Stateتحديث المصفوفات في Reactكورس React كاملأخطاء useState مع Objects

جدول مرجعي سريع

1

التعامل مع الكائنات والمصفوفات داخل useState وحفظ مبدأ Immutability

المفهوم الهندسيالآلية البرمجية في Reactالنتيجة والتطبيق المباشر
Immutabilityإنشاء نسخة جديدة من الكائن أو المصفوفة بدلاً من تعديل الأصل مباشرةضمان اكتشاف ريأكت لتغير المرجع في الذاكرة وإعادة رسم الواجهة بدقة
Spread Operator (...)نسخ كافة عناصر الكائن أو المصفوفة القديمة في هيكل جديد بسهولةتعديل الخصائص المراد تغييرها فقط مع الإبقاء على باقي البيانات دون تلف
تحديث المصفوفات بحذراستخدام دوال نقية مثل map و filter و concat بدلاً من push و spliceإمكانية إضافة أو حذف أو تعديل عناصر القوائم بأمان تام دون المساس بالمصفوفة الأصلية
Nested Updatesنسخ كل مستوى من مستويات الكائنات المتداخلة باستخدام النشر المتعددمنع المشاكل الخفية الناتجة عن مشاركة المراجع للبيانات المتداخلة

الشرح

عندما تكون القيمة المخزنة في useState نصاً أو رقماً بسيطاً (Primitive Values)، يكون التحديث مستقيماً. ولكن عندما تتعامل مع كائنات (Objects) أو مصفوفات (Arrays)، تختلف قواعد اللعبة تماماً!

في جافا سكريبت، الكائنات والمصفوفات تُخزن عن طريق المرجع في الذاكرة (Reference) وليس القيمة.

إذا قمت بتعديل خاصية داخل كائن مباشرة هكذا: user.name = "أحمد"، فإن المرجع الخص بالكائن في الذاكرة لم يتغير. وعندما تبعث هذا الكائن المكتوب عليه إلى دالة setUser(user)، ستقارن ريأكت المرجع القديم بالمرجع الجديد، وستجدهما متطابقين تماماً! والنتيجة؟ لن تقوم ريأكت بإعادة رندرة الشاشة إطلاقاً!

1. القاعدة الذهبية: التعامل مع الحالة كأنها Read-Only

لكي تشعر ريأكت بالتغير، يجب أن تعطيها كائناً جديداً تماماً بموقع ذاكرة جديد. ونحقق ذلك بفضل معامل النشر (Spread Operator ...).

2. التحديث في المصفوفات (Arrays)

  • دوال ممنوعة (تعدل على الأصل): push(), pop(), splice(), sort().
  • دوال معتمدة (ترجع مصفوفة جديدة): map(), filter(), concat(), [...oldArray].

الأكواد البرمجية والتطبيق العملي

دعنا نطبق التعامل مع الكائنات والمصفوفات لبناء لوحة ملف الطالب وقائمة كورساته المحددة في منصة نوفيل:

javascript
1import { useState } from 'react';
2
3function UserProfileManager() {
4 // 1. حالة على هيئة كائن يحتوي على بيانات الطالب
5 const [user, setUser] = useState({
6 name: 'أحمد محمود',
7 email: 'ahmed@nouvil.net',
8 role: 'طالب'
9 });
10
11 // 2. حالة على هيئة مصفوفة تحتوي على الكورسات المسجلة
12 const [enrolledCourses, setEnrolledCourses] = useState([
13 { id: 1, title: 'أساسيات HTML & CSS', completed: true },
14 { id: 2, title: 'احتراف JavaScript الحديثة', completed: false }
15 ]);
16
17 // تحديث خاصية داخل الكائن مع الحفاظ على باقي الخصائص
18 const handleRoleChange = (newRole) => {
19 setUser((prevUser) => ({
20 ...prevUser, // نسخ البيانات القديمة
21 role: newRole // تعديل الخاصية المطلوبة فقط
22 }));
23 };
24
25 // إضافة عنصر جديد للمصفوفة
26 const handleAddCourse = () => {
27 const newCourse = {
28 id: Date.now(),
29 title: 'بناء واجهات React متقدمة',
30 completed: false
31 };
32 // إنشاء مصفوفة جديدة تحتوي القديم + الجديد
33 setEnrolledCourses((prevCourses) => [...prevCourses, newCourse]);
34 };
35
36 // حذف عنصر من المصفوفة باستخدام filter
37 const handleDeleteCourse = (courseId) => {
38 setEnrolledCourses((prevCourses) =>
39 prevCourses.filter((course) => course.id !== courseId)
40 );
41 };
42
43 // تعديل حالة كورس محدد داخل المصفوفة باستخدام map
44 const handleToggleCourse = (courseId) => {
45 setEnrolledCourses((prevCourses) =>
46 prevCourses.map((course) => {
47 if (course.id === courseId) {
48 // إنتاج كائن جديد للكورس المعدل
49 return { ...course, completed: !course.completed };
50 }
51 return course; // إرجاع باقي العناصر كما هي
52 })
53 );
54 };
55
56 return (
57 <div style={{ padding: '20px', fontFamily: 'sans-serif', border: '2px solid #673ab7', borderRadius: '8px' }}>
58 <h2>تحديث البيانات المركبة (Objects & Arrays)</h2>
59
60 {/* عرض بيانات الكائن */}
61 <div style={{ backgroundColor: '#f5f5f5', padding: '15px', borderRadius: '5px' }}>
62 <h3>بيانات الملف الشخصي:</h3>
63 <p>الاسم: {user.name}</p>
64 <p>البريد: {user.email}</p>
65 <p>الرتبة الحالية: <strong>{user.role}</strong></p>
66 <button onClick={() => handleRoleChange('مطور متقدم')} style={{ padding: '5px 10px', cursor: 'pointer' }}>
67 رقّي الحساب إلى مطور متقدم
68 </button>
69 </div>
70
71 <hr style={{ margin: '20px 0' }} />
72
73 {/* عرض وتحديث المصفوفة */}
74 <div>
75 <h3>الكورسات المسجلة ({enrolledCourses.length}):</h3>
76 <button onClick={handleAddCourse} style={{ backgroundColor: '#4caf50', color: '#fff', border: 'none', padding: '8px 12px', border: 'none', borderRadius: '4px', cursor: 'pointer', marginBottom: '10px' }}>
77 + إضافة كورس جديد
78 </button>
79
80 <ul>
81 {enrolledCourses.map((course) => (
82 <li key={course.id} style={{ marginBottom: '8px' }}>
83 <span style={{ textDecoration: course.completed ? 'line-through' : 'none', marginLeft: '10px' }}>
84 {course.title}
85 </span>
86 <button onClick={() => handleToggleCourse(course.id)} style={{ marginLeft: '5px' }}>
87 {course.completed ? 'إلغاء الإكمال' : 'تحديد كـ مكتمل'}
88 </button>
89 <button onClick={() => handleDeleteCourse(course.id)} style={{ backgroundColor: '#f44336', color: '#fff', border: 'none', padding: '2px 8px', borderRadius: '3px', cursor: 'pointer' }}>
90 حذف
91 </button>
92 </li>
93 ))}
94 </ul>
95 </div>
96 </div>
97 );
98}
99
100export default UserProfileManager;

نصيحة الدرس وتركاية المطور

ℹ️ معلومة

تذكر أن المعامل النقطي (...Spread Operator) يقوم بـ نسخ سطحي (Shallow Copy) فقط! إذا كان الكائن يحتوي على كائن آخر بداخله:

javascript
1const [student, setStudent] = useState({
2 name: 'أحمد',
3 details: { age: 22, city: 'القاهرة' }
4});
javascript
1const [student, setStudent] = useState({
2 name: 'أحمد',
3 details: { age: 22, city: 'القاهرة' }
4});

لو قمت بكتابة: setStudent({ ...student, details: { city: 'الإسكندرية' } }) ستفقد الخاصية age تماماً لأنك استبدلت كائن details بالكامل! الحل الصحيح للكائنات المتداخلة: يجب عليك نسخ المستوى الخارجي ثم نسخ المستوى الداخلي بالتتابع:

الصياغة (Syntax)

syntax
1// ✅ التحديث الآمن للكائنات المتداخلة
2setStudent((prev) => ({
3 ...prev,
4 details: {
5 ...prev.details, // حفظ age
6 city: 'الإسكندرية' // تعديل city فقط
7 }
8}));

الدرس السابق

إدارة الحالة والتعرف على هوك useState

الدرس التالي

Lifting State Up

نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر