React: العرض المشروط وعرض القوائم

آخر تحديث: 2026-10-26

العرض الشرطي يشبه إشارات المرور — فهو يحدد ما يجب عرضه بناءً على الحالة؛ أما عرض القوائم فيشبه الإنتاج على خط التجميع — قائمة بيانات تُنشئ مكونات واجهة مستخدم متطابقة على دفعات.


1. ما ستتعلمه



2. قصة الواجهة الأمامية لمنصة التجارة الإلكترونية

(1) المشكلة: الحالات الست لقائمة المنتجات

تعمل أليس على صفحة قائمة المنتجات لمنصة للتجارة الإلكترونية. تحتوي هذه الصفحة على ست حالات عرض، وفي كل مرة تتغير الحالة، يتعين عليها التعامل مع نموذج DOM يدويًّا:

JAVASCRIPT
function renderProductList(state) {
  const container = document.getElementById('product-list')
  container.innerHTML = ''

  if (state.isLoading) {
    container.innerHTML = '<div class="loading">Loading......</div>'
  } else if (state.error) {
    container.innerHTML = '<div class="error">Failed to load</div>'
  } else if (!state.isLoggedIn) {
    container.innerHTML = '<div>Please log in</div>'
  } else if (state.products.length === 0) {
    container.innerHTML = '<div>No products available</div>'
  } else {
    let html = ''
    state.products.forEach(p => {
      html += `<div class="card"><h3>${p.name}</h3><p>$${p.price}</p></div>`
    })
    container.innerHTML = html
  }
}

سؤال أليس: في كل مرة تتغير فيها الحالة، أضطر إلى مسح الحاوية يدويًّا وإنشاء كود HTML. 30 سطرًا من الكود تؤدي وظيفة واحدة فقط — وهي عرض الحالة الحالية.

(2) حلول للعرض المشروط في React

JSX
function ProductList({ user, products, isLoading, error }) {
  // Each state is independent if Branch,No interference with one another
  if (isLoading) return <LoadingSpinner />
  if (error) return <ErrorMessage message={error} />
  if (!user) return <LoginPrompt />
  if (products.length === 0) return <EmptyState />
  
  return (
    <div className="product-grid">
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  )
}
▶ جرّب الكود

المزايا: 15 سطراً فقط من التعليمات البرمجية تغطي جميع الحالات، كما أن كل فرع يسهل فهمه بنظرة واحدة.



3. ثلاث طرق لاستخدام العرض الشرطي

الطريقة الصيغة حالات الاستخدام ملاحظات
عامل ثلاثي condition ? A : B اختر أحد الخيارين (مع فرع "else") يصعب قراءة العوامل الثلاثية المتداخلة؛ تجنب استخدام أكثر من مستويين
المنطق و&& condition && A لا تُعرض إلا إذا تم استيفاء الشرط (بدون "else") 0 && <Comp /> ستُعرض بقيمة 0؛ استخدم > 0
«أو» المنطقي ` ` `value

(1) عامل ثلاثي ? :

السيناريوهات المناسبة لـ«اختيار أحد خيارين»:

JSX
function StatusBadge({ isActive }) {
  return (
    <span style={{ color: isActive ? '#52c41a' : '#ff4d4f' }}>
      {isActive ? '🟢 Online' : '🔴 Offline'}
    </span>
  )
}

// isActive=true  → <span>🟢 Online</span>
// isActive=false → <span>🔴 Offline</span>
▶ جرّب الكود

(2) المنطق و&&

مناسب لعبارة «لا تُعرض إلا إذا استوفيت الشروط؛ وإلا فلا تُعرض»:

JSX
function Notification({ count }) {
  return (
    <div className="notification-icon">
      🔔
      {/* count > 0 A red dot appears,Otherwise, nothing will be displayed. */}
      {count > 0 && (
        <span className="badge">{count > 99 ? '99+' : count}</span>
      )}
    </div>
  )
}

// count=0  → Show only 🔔
// count=5  → Show 🔔 and  "5"
▶ جرّب الكود
⚠️ مصدر خطأ: لا تكتب {items.length && <List />}. عندما تكون قيمة items.length هي 0، تكون نتيجة 0 && <List /> هي 0، وسيقوم React بعرض الرقم 0 على الصفحة. الطريقة الصحيحة لكتابة ذلك هي {items.length > 0 && <List />}.

(3) «أو» المنطقي ||

مناسب لـ«توفير قيمة بديلة»:

JSX
function UserProfile({ user }) {
  return (
    <div>
      <h2>{user.nickname || 'Anonymous User'}</h2>
      <p>{user.bio || 'This person is lazy.,Nothing was written...'}</p>
      <p>Location:{user.location || 'Unknown'}</p>
    </div>
  )
}
▶ جرّب الكود

الناتج:

TEXT
مقارنة جنبًا إلى جنب: صياغة HTML وصياغة JSX تعرضان ترميزًا مكافئًا. مكون React من نوع TypeScript يعرّف props عبر interface

▶ مثال: مقارنة جنبًا إلى جنب بين ثلاثة أنواع من العرض الشرطي

JSX
// ============================================
// Example:Combined Use of Three Types of Conditional Rendering in the User Panel
// ============================================

function UserPanel({ user }) {
  return (
    <div className="panel">
      {/* 1. Ternary:Choose one of the two */}
      <h2>
        {user.isVIP ? '⭐ Premium Member' : 'Regular User'}
      </h2>

      {/* 2. &&:Display only when met */}
      {user.isVIP && (
        <p>Membership Expiration:{user.vipExpireDate}</p>
      )}

      {/* 3. ||:Floor value */}
      <p>Signature:{user.slogan || 'This person is really cool.,No signature'}</p>

      {/* Combined Use:List of Tags */}
      {user.tags && user.tags.length > 0 && (
        <div>
          {user.tags.map(tag => (
            <span key={tag} style={{ background: '#f0f0f0', padding: '2px 8px', margin: '2px', borderRadius: '4px' }}>
              {tag}
            </span>
          ))}
        </div>
      )}
    </div>
  )
}
▶ جرّب الكود

الناتج:

TEXT
إذا كان VIP: "⭐ Premium Member" + تاريخ انتهاء العضوية. وإلا: "Regular User". التوقيع: نص المستخدم أو محتوى احتياطي. العلامات تظهر فقط عند وجودها.


4. عرض القوائم: .map()

(1) الاستخدام الأساسي

Array.map() إجراء تكرار على مصفوفة وإرجاع كود JSX لكل عنصر:

▶ جرّب الكود

(2) مصفوفة من كائنات العرض

▶ جرّب الكود

(3) 3 تقنيات لتحويل البيانات من أجل عرض القوائم

JSX
const items = [
  { id: 1, name: 'React Basics', price: 0 },
  { id: 2, name: 'Vue Getting Started', price: 0 },
  { id: 3, name: 'Node.js Real-World Experience', price: 99 }
]

// 1. Direct Rendering
{items.map(item => <li key={item.id}>{item.name}</li>)}

// 2. Filter First, Then Render(Free Courses)
{items
  .filter(item => item.price === 0)
  .map(item => <li key={item.id}>{item.name}(Free)</li>)
}

// 3. Extract Rendering Functions
function renderItem(item) {
  return (
    <li key={item.id}>
      {item.name} - ${item.price}
    </li>
  )
}
{items.map(renderItem)}
▶ جرّب الكود

5. أهمية المفتاح

يستخدم React key لتعريف كل عنصر في القائمة. عندما يظل المفتاح ثابتًا، يعيد React استخدام عقدة DOM؛ وعندما يتغير المفتاح، يقوم React بإتلافها وإعادة إنشائها.

100%
graph LR
    A[List Changes] --> B{key Changes?}
    B -->|No, key The same| C[Reuse DOM Node<br/>Update only the changes]
    B -->|Yes, key It's changed| D[Delete Old Nodes<br/>Create a New Node]
    B -->|No, None key| E[Render All Over]

الناتج:

TEXT
العنوان الفرعي: "        {user.isVIP ? '⭐ Premium Member' : 'Regular User'}      ". يعرض: "0 && ("
الاستراتيجية الرئيسية الأداء التوصية
المعرّف الفريد لقاعدة البيانات ✅ الأمثل ⭐⭐⭐
سلسلة فريدة (UUID) ✅ الأفضل ⭐⭐⭐
مؤشر المصفوفة ⚠️ محفوف بالمخاطر (قد تصبح الحالة غير متسقة عند تغيير القائمة) ⭐
رقم عشوائي ❌ النطاق (يختلف مع كل عملية عرض) ❌
لم يتم تمرير أي مفتاح ❌ أداء ضعيف ❌

▶ مثال: باستخدام مفتاح مقابل عدم استخدام مفتاح

▶ جرّب الكود

الناتج:

TEXT
قائمتان تحتويان حقول إدخال. النقر على "Randomize": ✅ القائمة (key=id) تحافظ على محاذاة قيم الإدخال؛ ❌ القائمة (key=index) تكون قيم الإدخال فيها غير محاذية للنص


6. حالة الفراغ وحالة التحميل

الحالة الشرط سلوك واجهة المستخدم كيفية التعامل
قيد التحميل loading === true شاشة دوارة / شاشة هيكلية if (loading) return <Spinner />
فشل التحميل error !== null رسالة خطأ + زر «إعادة المحاولة» if (error) return <ErrorView />
بيانات فارغة data.length === 0 رسم توضيحي للحالة الفارغة + نص if (!data.length) return <Empty />
عادي لا شيء مما سبق عرض قائمة البيانات الافتراضي: العودة إلى واجهة المستخدم العادية

في المشاريع العملية، هناك ثلاث حالات يجب التعامل معها عند تحميل البيانات:

JSX
// ============================================
// Example:Complete List Component(3 Handling Different States)
// ============================================

function ArticleList() {
  const [articles, setArticles] = React.useState([])
  const [isLoading, setIsLoading] = React.useState(true)
  const [error, setError] = React.useState(null)

  // Simulated Loading(Actually, it is API Call)
  React.useEffect(() => {
    fetch('/api/articles')
      .then(res => {
        if (!res.ok) throw new Error('Network request failed')
        return res.json()
      })
      .then(data => {
        setArticles(data)
        setIsLoading(false)
      })
      .catch(err => {
        setError(err.message)
        setIsLoading(false)
      })
  }, [])

  // 1. Loading...
  if (isLoading) {
    return (
      <div style={{ textAlign: 'center', padding: '40px' }}>
        <div className="spinner" />
        <p>Loading article...</p>
      </div>
    )
  }

  // 2. Failed to load
  if (error) {
    return (
      <div style={{ textAlign: 'center', padding: '40px', color: '#ff4d4f' }}>
        <p>❌ Failed to load:{error}</p>
        <button onClick={() => window.location.reload()}>Reload</button>
      </div>
    )
  }

  // 3. Empty data
  if (articles.length === 0) {
    return (
      <div style={{ textAlign: 'center', padding: '40px', color: '#999' }}>
        <p>📭 No articles available</p>
        <p>No one has posted an article yet.,Go ahead and write your first one!!</p>
      </div>
    )
  }

  // 4. Normal Rendering
  return (
    <div>
      <h3>List of Articles(Total  {articles.length}  articles)</h3>
      {articles.map(article => (
        <ArticleCard key={article.id} article={article} />
      ))}
    </div>
  )
}
▶ جرّب الكود

7. مثال كامل: فئات المنتجات والبحث

▶ عرض المثال القابل للتشغيل (81 سطرًا)
▶ جرّب الكود

الناتج:

TEXT
يعرض: "Randomize the order". زر: Randomize the order. الإدخال: Enter content، The content may be misaligned

الناتج المتوقع: صفحة قائمة منتجات كاملة تدعم تصفية البحث، وتصفية الفئات، وإشعارات النتائج الفارغة، والإحصاءات.


▶ المثال 5: عرض حقول النموذج ديناميكيًا

JSX
function DynamicForm() {
  const [formType, setFormType] = useState('personal')
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    company: '',
    taxId: '',
    budget: '',
  })

  function handleChange(e) {
    const { name, value } = e.target
    setFormData(prev => ({ ...prev, [name]: value }))
  }

  const personalFields = (
    <>
      <input name="name" value={formData.name} onChange={handleChange} placeholder="Name" />
      <input name="email" value={formData.email} onChange={handleChange} placeholder="Email" />
    </>
  )

  const businessFields = (
    <>
      <input name="company" value={formData.company} onChange={handleChange} placeholder="Company" />
      <input name="taxId" value={formData.taxId} onChange={handleChange} placeholder="Tax ID" />
      <input name="budget" value={formData.budget} onChange={handleChange} placeholder="Budget ($)" />
    </>
  )

  return (
    <div style={{ maxWidth: 400, margin: '0 auto' }}>
      <h3>Registration</h3>
      <div style={{ marginBottom: 12 }}>
        {['personal', 'business'].map(type => (
          <button
            key={type}
            onClick={() => setFormType(type)}
            style={{
              padding: '6px 16px', marginRight: 8, cursor: 'pointer',
              backgroundColor: formType === type ? '#1890ff' : '#f0f0f0',
              color: formType === type ? 'white' : '#333', border: 'none', borderRadius: 4,
            }}
          >
            {type === 'personal' ? 'Personal' : 'Business'}
          </button>
        ))}
      </div>
      {formType === 'personal' ? personalFields : businessFields}
      <button style={{ marginTop: 12, padding: '8px 24px', cursor: 'pointer' }}>Submit</button>
    </div>
  )
}
▶ جرّب الكود

الناتج:

TEXT
نموذج شخصي: حقول Name + Email. نموذج شركة: حقول Company + Tax ID + Budget. يتم التبديل بينهما بزر. في الأسفل زر الإرسال.

الناتج:

TEXT
حقل البحث يصفي العناصر. أزرار الترتيب: A-Z (حسب الاسم) / Stars (حسب التقييم). الترتيب الحالي مُبرَز بالأزرق. القائمة بعد التصفية + الترتيب تتحدث بشكل تفاعلي

▶ المثال 3: البحث في القوائم وتصفيتها وفرزها

▶ جرّب الكود

الناتج:

TEXT
الأطر: React ⭐220k، Vue ⭐207k، Tailwind ⭐80k، Django ⭐78k، Express ⭐63k. تصفية عبر البحث؛ تبديل الترتيب A-Z/Stars.

الناتج:

TEXT
يعرض "{cat.name}"

▶ المثال 4: عرض القوائم المتداخلة — عرض المنتجات المصنفة

▶ جرّب الكود

الناتج:

TEXT
Electronics: Keyboard $79، Monitor $399
Furniture: Desk $249، Chair $189
Books: "No products yet" (حالة فارغة بخط مائل)


❓ أسئلة شائعة

س هل يمكن تداخل .map()؟ كيف يتم التعامل مع المفاتيح عند استخدام عدة استدعاءات لـ .map()؟
ج نعم، يمكن تداخلها. يجب أن يكون لكل مستوى من .map() مفتاحه الخاص. على سبيل المثال، عرض "الفئة → المنتج: cats.map(c => <div key={c.id}><h3>{c.name}</h3>{c.products.map(p => <span key={p.id}>{p.name}</span>)}</div>)". يمكن أن تكون المفاتيح في المستويات المختلفة متطابقة (شريطة أن تكون فريدة داخل المصفوفات الخاصة بها).
س أيهما أفضل — التعبيرات الثلاثية أم صيغة &&؟
ج لكل منهما حالة استخدام خاصة به: && مناسبة لـ «العرض إذا تم استيفاء الشرط؛ وعدم عرض أي شيء إذا لم يتم استيفاؤه»؛ أما التعبير الثلاثي ? : فهو مناسب لـ «عرض المكون X إذا تم استيفاء الشرط A؛ وعرض المكون Y إذا تم استيفاء الشرط B». إذا كنت تريد «عدم عرض أي شيء عند عدم استيفاء الشرط» باستخدام العامل الثلاثي، فستكون صيغة الكتابة condition ? <Comp /> : null، لكن && أكثر إيجازًا في هذه الحالة. ومع ذلك، انتبه إلى «فخ عرض الرقم 0» مع && — حيث ستعرض 0 && <Comp /> الرقم 0.

📖 ملخص


📝 تمارين

  1. المشكلة الأساسية (صعوبة ⭐): أنشئ مكونًا TemperatureDisplay يعرض، استنادًا إلى خصائص temp، رمز 🔥 باللون الأحمر عندما تكون درجة الحرارة >35 درجة مئوية، ورمز ☀️ باللون الأصفر عندما تكون درجة الحرارة بين 15 و35 درجة مئوية، ورمز ❄️ باللون الأزرق عندما تكون درجة الحرارة <15 درجة مئوية.
  2. مشكلة متقدمة (درجة الصعوبة ⭐⭐): أنشئ مكونًا TodoFilter يقبل خاصيتين — todos (مصفوفة من المهام) وfilter (كلها/مكتملة/غير مكتملة) — وقم بتنفيذ عملية التصفية بناءً على الحالة.
  3. التحدي (الصعوبة: ⭐⭐⭐): أنشئ مكونًا Pagination يقبل الخصائص totalPages وcurrentPage وonPageChange ويعرض أزرار أرقام الصفحات، مع دعم الصفحتين الأولى والأخيرة بالإضافة إلى علامة الحذف.
Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%