React

القوائم والشروط في React — دليل شامل 2026

📅 2026-10-16⏱ 9 دقائق قراءة
في المقال السابق، تعلمت useEffect وإدارة التأثيرات الجانبية. الآن سنتعلم **القوائم (Lists)** و **الشروط (Conditionals)** — وهي الطريقة التي نعرض بها البيانات ديناميكياً في React. في هذا الدليل العملي، سنأخذك خطوة بخطوة لفهم كيفية عرض القوائم والشروط في React، مع تمارين وحلول. ## عرض القوائم ### 1. الطريقة الأساسية — `map` ```tsx function FruitList() { const fruits = ["تفاح", "موز", "برتقال"]; return ( <ul> {fruits.map((fruit) => ( <li key={fruit}>{fruit}</li> ))} </ul> ); } ``` **شرح:** - **`map`:** تُحوّل كل عنصر في المصفوفة إلى JSX. - **`key`:** معرّف فريد لكل عنصر (مهم للأداء). ### 2. قائمة كائنات ```tsx interface User { id: number; name: string; email: string; } function UserList() { const users: User[] = [ { id: 1, name: "أحمد", email: "ahmed@example.com" }, { id: 2, name: "محمد", email: "mohamed@example.com" }, { id: 3, name: "علي", email: "ali@example.com" }, ]; return ( <ul> {users.map((user) => ( <li key={user.id}> <strong>{user.name}</strong> - {user.email} </li> ))} </ul> ); } ``` ### 3. استخدام مكوّن منفصل ```tsx interface UserProps { user: User; } function UserItem({ user }: UserProps) { return ( <li> <strong>{user.name}</strong> - {user.email} </li> ); } function UserList() { const users: User[] = [...]; return ( <ul> {users.map((user) => ( <UserItem key={user.id} user={user} /> ))} </ul> ); } ``` ## أهمية `key` **`key`** هو **معرّف فريد** لكل عنصر في القائمة. ### لماذا `key` مهم؟ - **الأداء:** React يستخدمه لمعرفة العناصر التي تغيرت. - **الحالة:** يحافظ على state العناصر. - **التحذيرات:** بدون `key`، React يعطي تحذيراً. ### ❌ طرق خاطئة: ```tsx // ❌ بدون key: {items.map((item) => <li>{item}</li>)} // ❌ استخدام index (خطر): {items.map((item, index) => <li key={index}>{item}</li>)} ``` **⚠️ لماذا `index` خطير؟** - عند إضافة/حذف عنصر، تتغير الـ indices. - React قد يعيد استخدام العناصر بشكل خاطئ. - يفقد state العنصر. ### ✅ الطريقة الصحيحة: ```tsx // ✅ باستخدام id فريد: {items.map((item) => <li key={item.id}>{item.name}</li>)} // ✅ باستخدام قيمة فريدة: {fruits.map((fruit) => <li key={fruit}>{fruit}</li>)} ``` ## الشروط في JSX ### 1. `if` خارج JSX ```tsx function Greeting({ isLoggedIn }: { isLoggedIn: boolean }) { if (isLoggedIn) { return <h1>مرحباً بك!</h1>; } return <h1>سجّل الدخول</h1>; } ``` ### 2. Ternary Operator (`? :`) ```tsx function Greeting({ isLoggedIn }: { isLoggedIn: boolean }) { return ( <h1>{isLoggedIn ? "مرحباً بك!" : "سجّل الدخول"}</h1> ); } ``` **الاستخدام:** خياران فقط. ### 3. `&&` — العرض الشرطي ```tsx function Inbox({ messages }: { messages: string[] }) { return ( <div> <h1>صندوق الوارد</h1> {messages.length > 0 && ( <p>لديك {messages.length} رسالة</p> )} {messages.length === 0 && <p>لا توجد رسائل</p>} </div> ); } ``` **⚠️ تحذير:** إذا كانت القيمة `0`، ستُعرض `0` في JSX! ```tsx // ❌ خطأ (يعرض "0" إذا كان length === 0): {messages.length && <p>...</p>} // ✅ صحيح: {messages.length > 0 && <p>...</p>} ``` ### 4. `if-else if-else` — حالات متعددة ```tsx function StatusBadge({ status }: { status: "pending" | "approved" | "rejected" }) { if (status === "pending") { return <span className="yellow">⏳ قيد المراجعة</span>; } if (status === "approved") { return <span className="green">✅ مقبول</span>; } return <span className="red">❌ مرفوض</span>; } ``` ### 5. `switch` — حالات كثيرة ```tsx function Icon({ type }: { type: string }) { switch (type) { case "user": return <span>👤</span>; case "email": return <span>📧</span>; case "phone": return <span>📱</span>; default: return <span>❓</span>; } } ``` ## التصيير المتقدم ### 1. تصفية القائمة ```tsx function ProductList({ products, showInStock }: { products: Product[]; showInStock: boolean }) { const filtered = showInStock ? products.filter((p) => p.inStock) : products; return ( <ul> {filtered.map((p) => ( <li key={p.id}>{p.name}</li> ))} </ul> ); } ``` ### 2. ترتيب القائمة ```tsx const sorted = [...products].sort((a, b) => a.price - b.price); ``` ### 3. البحث ```tsx const [query, setQuery] = useState(""); const filtered = products.filter((p) => p.name.toLowerCase().includes(query.toLowerCase()) ); ``` ### 4. تجميع القائمة ```tsx const grouped = products.reduce<Record<string, Product[]>>((acc, p) => { const category = p.category; if (!acc[category]) acc[category] = []; acc[category].push(p); return acc; }, {}); ``` ## مثال عملي: قائمة مهام كاملة ```tsx interface Todo { id: number; text: string; completed: boolean; } function TodoApp() { const [todos, setTodos] = useState<Todo[]>([ { id: 1, text: "تعلم React", completed: true }, { id: 2, text: "بناء مشروع", completed: false }, { id: 3, text: "نشر الموقع", completed: false }, ]); const [filter, setFilter] = useState<"all" | "active" | "completed">("all"); const filteredTodos = todos.filter((todo) => { if (filter === "active") return !todo.completed; if (filter === "completed") return todo.completed; return true; }); return ( <div className="todo-app"> <h1>📝 قائمة المهام</h1> <div className="filters"> <button onClick={() => setFilter("all")}>الكل</button> <button onClick={() => setFilter("active")}>النشطة</button> <button onClick={() => setFilter("completed")}>المكتملة</button> </div> {filteredTodos.length === 0 ? ( <p className="empty">لا توجد مهام</p> ) : ( <ul> {filteredTodos.map((todo) => ( <li key={todo.id} className={todo.completed ? "done" : ""}> <input type="checkbox" checked={todo.completed} onChange={() => setTodos( todos.map((t) => t.id === todo.id ? { ...t, completed: !t.completed } : t ) ) } /> <span>{todo.text}</span> </li> ))} </ul> )} </div> ); } ``` ## مثال عملي: قائمة منتجات ```tsx interface Product { id: number; name: string; price: number; image: string; inStock: boolean; } function ProductGrid() { const products: Product[] = [ { id: 1, name: "لابتوب", price: 5000, image: "...", inStock: true }, { id: 2, name: "هاتف", price: 2000, image: "...", inStock: true }, { id: 3, name: "تابلت", price: 1500, image: "...", inStock: false }, ]; return ( <div className="grid"> {products.map((product) => ( <div key={product.id} className="product-card"> <img src={product.image} alt={product.name} /> <h3>{product.name}</h3> <p>{product.price} $</p> {product.inStock ? ( <span className="in-stock">✅ متوفر</span> ) : ( <span className="out-of-stock">❌ غير متوفر</span> )} </div> ))} </div> ); } ``` ## عرض البيانات الشرطية المتقدمة ### 1. حالات API (Loading/Error/Success) ```tsx type State = | { status: "idle" } | { status: "loading" } | { status: "success"; data: string[] } | { status: "error"; message: string }; function DataView() { const [state, setState] = useState<State>({ status: "idle" }); if (state.status === "loading") return <p>جاري التحميل...</p>; if (state.status === "error") return <p>خطأ: {state.message}</p>; if (state.status === "success") { return ( <ul> {state.data.map((item, i) => ( <li key={i}>{item}</li> ))} </ul> ); } return <button onClick={() => setState({ status: "loading" })}>ابدأ</button>; } ``` ### 2. عرض شرطي متداخل ```tsx function UserProfile({ user }: { user: User | null }) { if (!user) return <p>لم يتم تسجيل الدخول</p>; return ( <div> <h2>{user.name}</h2> {user.isAdmin && <span className="badge">👑 مدير</span>} {user.posts.length > 0 ? ( <ul> {user.posts.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ) : ( <p>لا توجد مقالات</p> )} </div> ); } ``` ## تمارين عملية ### تمرين 1: قائمة أسماء اعرض مصفوفة أسماء في قائمة. **الحل:** ```tsx function Names() { const names = ["أحمد", "محمد", "علي"]; return <ul>{names.map((n) => <li key={n}>{n}</li>)}</ul>; } ``` ### تمرين 2: قائمة أرقام مع شرط اعرض الأرقام الزوجية فقط. **الحل:** ```tsx function EvenNumbers() { const nums = [1, 2, 3, 4, 5, 6]; const evens = nums.filter((n) => n % 2 === 0); return <ul>{evens.map((n) => <li key={n}>{n}</li>)}</ul>; } ``` ### تمرين 3: عرض شرطي اعرض "مرحباً" إذا كان `isLoggedIn` صحيحاً، وإلا "سجّل الدخول". **الحل:** ```tsx function Greeting({ isLoggedIn }: { isLoggedIn: boolean }) { return <h1>{isLoggedIn ? "مرحباً" : "سجّل الدخول"}</h1>; } ``` ### تمرين 4: قائمة مهام مع تصفية اعرض المهام النشطة فقط. **الحل:** ```tsx function ActiveTodos() { const todos = [ { id: 1, text: "مهمة 1", done: true }, { id: 2, text: "مهمة 2", done: false }, { id: 3, text: "مهمة 3", done: false }, ]; const active = todos.filter((t) => !t.done); return <ul>{active.map((t) => <li key={t.id}>{t.text}</li>)}</ul>; } ``` ### تمرين 5: حالة فارغة اعرض "لا توجد بيانات" إذا كانت القائمة فارغة. **الحل:** ```tsx function EmptyList({ items }: { items: string[] }) { if (items.length === 0) return <p>لا توجد بيانات</p>; return <ul>{items.map((i) => <li key={i}>{i}</li>)}</ul>; } ``` ### تمرين 6: بحث في القائمة اعرض فقط المنتجات التي تحتوي على نص البحث. **الحل:** ```tsx function SearchProducts() { const [query, setQuery] = useState(""); const products = ["لابتوب", "هاتف", "تابلت", "سماعات"]; const filtered = products.filter((p) => p.toLowerCase().includes(query.toLowerCase()) ); return ( <div> <input value={query} onChange={(e) => setQuery(e.target.value)} /> <ul>{filtered.map((p) => <li key={p}>{p}</li>)}</ul> </div> ); } ``` ### تمرين 7: ترتيب القائمة اعرض الأرقام مرتبة تصاعدياً. **الحل:** ```tsx function SortedNumbers() { const nums = [5, 2, 8, 1, 9, 3]; const sorted = [...nums].sort((a, b) => a - b); return <ul>{sorted.map((n) => <li key={n}>{n}</li>)}</ul>; } ``` ### تمرين 8: جدول مستخدمين اعرض جدول مستخدمين مع حالاتهم. **الحل:** ```tsx interface User { id: number; name: string; active: boolean; } function UsersTable() { const users: User[] = [ { id: 1, name: "أحمد", active: true }, { id: 2, name: "محمد", active: false }, { id: 3, name: "علي", active: true }, ]; return ( <table> <thead> <tr> <th>الاسم</th> <th>الحالة</th> </tr> </thead> <tbody> {users.map((user) => ( <tr key={user.id}> <td>{user.name}</td> <td>{user.active ? "✅ نشط" : "❌ غير نشط"}</td> </tr> ))} </tbody> </table> ); } ``` ## حل المشاكل الشائعة ### 🔴 المشكلة 1: `Warning: Each child in a list should have a unique "key" prop` **السبب:** عدم وجود `key` في القائمة. **الحل:** أضف `key` فريد. ### 🔴 المشكلة 2: `Objects are not valid as a React child` **السبب:** عرض كائن مباشرة. **الحل:** اعرض خاصية من الكائن. ### 🔴 المشكلة 3: عرض `0` بدلاً من لا شيء **السبب:** استخدام `&&` مع رقم. **الحل:** ```tsx // ❌ {items.length && <p>...</p>} // ✅ {items.length > 0 && <p>...</p>} ``` ### 🔴 المشكلة 4: `Cannot read property 'map' of undefined` **السبب:** القيمة غير معرّفة. **الحل:** ```tsx {items?.map(...) || <p>لا توجد بيانات</p>} ``` ## جدول طرق القوائم <table> <thead> <tr> <th>الطريقة</th> <th>الوظيفة</th> </tr> </thead> <tbody> <tr> <td><code>map</code></td> <td>تحويل كل عنصر</td> </tr> <tr> <td><code>filter</code></td> <td>تصفية العناصر</td> </tr> <tr> <td><code>sort</code></td> <td>ترتيب</td> </tr> <tr> <td><code>reduce</code></td> <td>تجميع</td> </tr> <tr> <td><code>slice</code></td> <td>جزء من القائمة</td> </tr> <tr> <td><code>find</code></td> <td>إيجاد عنصر</td> </tr> </tbody> </table> ## قائمة تحقق نهائية <table> <thead> <tr> <th>المهمة</th> <th>الحالة</th> </tr> </thead> <tbody> <tr> <td>عرض القوائم باستخدام map</td> <td>⬜</td> </tr> <tr> <td>فهم أهمية key</td> <td>⬜</td> </tr> <tr> <td>الشروط باستخدام ? :</td> <td>⬜</td> </tr> <tr> <td>الشروط باستخدام &&</td> <td>⬜</td> </tr> <tr> <td>تصفية وترتيب القوائم</td> <td>⬜</td> </tr> <tr> <td>الحالات المعقدة (Loading/Error)</td> <td>⬜</td> </tr> <tr> <td>حل التمارين الثمانية</td> <td>⬜</td> </tr> </tbody> </table> ## ماذا بعد هذا المقال؟ الآن بعد أن أتقنت القوائم والشروط، أنت جاهز للمقال التالي: 1. **React Router** — التنقل بين الصفحات. 2. **Context API** — إدارة الحالة العامة. 3. **مشروع متكامل** — تطبيق كامل. ## الخلاصة في هذا المقال، تعلمت: - ✅ عرض القوائم باستخدام `map`. - ✅ أهمية `key` ولماذا `index` خطر. - ✅ الشروط في JSX (`? :`, `&&`, `if`). - ✅ تصفية وترتيب القوائم. - ✅ الحالات المعقدة (Loading/Error/Success). - ✅ حل 8 تمارين عملية. **تذكر:** القوائم والشروط هي أساس كل تطبيق ديناميكي. أتقنها جيداً. **هل أتقنت القوائم والشروط؟ شاركنا في التعليقات!**