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 تمارين عملية.
**تذكر:** القوائم والشروط هي أساس كل تطبيق ديناميكي. أتقنها جيداً.
**هل أتقنت القوائم والشروط؟ شاركنا في التعليقات!**