JSX.Element مقابل ReactNode مقابل ReactElement: معركة الأنواع التي يجب أن تربحها
مرحباً يا زميل! إذا كنت تكتب كود React مع TypeScript، فبالتأكيد واجهت موقفاً يبدأ فيه المترجم بالشكوى من أنواع القيم المُعادة. "متوقع JSX.Element، ولكن تم استلام ReactNode" — خطأ مألوف؟ أو ربما رأيت ReactElement في الأنواع العامة وتساءلت: "ما الفرق أصلاً؟"
مفسد: هذه ليست مجرد مصطلحات أكاديمية. الاختيار الصحيح للنوع يمكن أن يسرع كودك، ويمنع الأخطاء، ويجعل المكونات أكثر قابلية للتنبؤ. اليوم سنشرح هذا الموضوع بأمثلة حقيقية، حتى لا تخلط بين هذه الأنواع الثلاثة مرة أخرى. هيا بنا!
شرح سريع: ما هو كل نوع
لنبدأ بالأساسيات. جميع الأنواع الثلاثة هي أجزاء من نظام بيئي واحد، ولكن بدرجات مختلفة من التجريد.
ReactElement
هذه هي اللبنة الأساسية. ReactElement هو كائن يحتوي على النوع (type) والخصائص (props) والمفتاح (key). إنه يمثل "مخططاً" للعنصر الذي سيحوله React لاحقاً إلى عقدة DOM حقيقية. إذا كتبت React.createElement('div', null, 'Hello')، فستحصل على ReactElement.
import React from 'react';
const element: React.ReactElement = React.createElement('div', { className: 'box' }, 'مرحباً!');// النوع: ReactElement<{ className: string }, 'div'>مهم: ReactElement هو نوع عام (generic). يمكنك تحديد نوع الخصائص ونوع العنصر:
type MyElement = React.ReactElement<{ name: string }, 'button'>;JSX.Element
هذا هو امتداد لـ ReactElement يضيف خصوصية JSX. بشكل أساسي، JSX.Element هو ReactElement<any, any>. عندما تكتب <div /> في JSX، يرى TypeScript هذا على أنه JSX.Element.
const jsxElement: JSX.Element = <div>JSX.Element</div>;الفرق الرئيسي عن ReactElement هو أن JSX.Element غير قابل للمعلمات (non-parameterized). إنه ببساطة "أي عنصر JSX".
ReactNode
هذا هو النوع الأوسع. ReactNode يشمل:
- ReactElement
- النصوص (strings)
- الأرقام (numbers)
- القيم المنطقية (true/false)
- null و undefined
- المصفوفات مما سبق
- React Portal
بكلمات بسيطة: ReactNode هو كل ما يمكن لـ React عرضه. هذا النوع يُستخدم تحديداً لـ children في المكونات.
type ReactNode = | React.ReactElement | string | number | boolean | null | undefined | React.ReactNode[];متى تستخدم ماذا؟ سيناريوهات عملية
الآن الجزء الممتع — دعنا نحدد أي نوع تختار في المواقف الحقيقية.
السيناريو 1: القيمة المُعادة من المكون
المكون الخاص بك يُعيد JSX. أي نوع يجب تحديده؟
// ❌ سيء: نوع عام جداًfunction MyComponent(): ReactNode { return <div>مرحباً</div>;}
// ✅ جيد: استخدم JSX.Elementfunction MyComponent(): JSX.Element { return <div>مرحباً</div>;}
// ✅ ممتاز: إذا كنت بحاجة إلى كتابة أنواع صارمة للخصائصfunction MyComponent(): React.ReactElement<{ title: string }> { return <div>مرحباً</div>;}لماذا؟ المكون يُعيد دائماً JSX (أو null إذا لم يعرض شيئاً). ReactNode واسع جداً — فهو يسمح بإعادة نص أو رقم أو مصفوفة، مما قد يؤدي إلى أخطاء غير متوقعة.
السيناريو 2: children في المكون
هنا ReactNode هو أفضل صديق لك. العناصر الفرعية يمكن أن تكون أي شيء: نص، مكون آخر، null (عرض شرطي) أو مصفوفة.
interface CardProps { title: string; children: React.ReactNode; // ✅ صحيح}
function Card({ title, children }: CardProps): J