ما الذي يخفيه prop children في React و TypeScript؟
\n\nإذا سبق لك كتابة مكون (Component) في React باستخدام TypeScript، فمن المحتمل أنك واجهت السؤال: "ما هو نوع البيانات الخاص بـ prop children؟". هذا السؤال هو من أكثر الأسئلة شيوعًا على Stack Overflow، وليس بدون سبب. للوهلة الأولى، يبدو أن الإجابة بسيطة: ReactNode. ولكن الشيطان، كما يُقال، يكمن في التفاصيل.
في هذه المقالة، لن نقدم إجابة جافة فقط. سنشرح جميع الأنواع الممكنة لـ children، والاختلافات بينها، وأفضل الممارسات، والمزالق المحتملة. ستتعرف متى تستخدم ReactNode، ومتى تستخدم ReactElement، ومتى تستخدم أنواعًا أكثر تحديدًا. هيا بنا!
لماذا هذا السؤال شائع جدًا؟
\n\nالمشكلة هي أن children هو prop خاص في React. يمكن أن يكون أي شيء:
- \n
- نصًا:
"مرحبًا بالعالم!"\n - رقمًا:
{42}\n - عنصر JSX:
<div>...</div>\n - مصفوفة من العناصر:
[<span/>, <span/>]\n - دالة (render prop):
{() => <div/>}\n - حتى
nullأوundefinedأوboolean\n
بسبب هذه المرونة، غالبًا ما يكتب المبتدئون children: any، مما يلغي فائدة TypeScript بأكملها. دعنا نفهم الأساليب الصحيحة.
الأنواع الرئيسية لـ children
\n\n1. ReactNode — الحل الشامل
\n\nهذا هو النوع الأكثر شيوعًا وصحة لـ children في 90% من الحالات. يشمل كل ما يمكن لـ React عرضه.
import { ReactNode } from 'react';\n\ninterface CardProps {\n title: string;\n children: ReactNode;\n}\n\nfunction Card({ title, children }: CardProps) {\n return (\n <div className="card">\n <h2>{title}</h2>\n <div className="card-content">\n {children}\n </div>\n </div>\n );\n}\n\n// الاستخدام:\n<Card title="بطاقتي">\n <p>هذا النص سيتم عرضه</p>\n {123} {/* الأرقام تعمل أيضًا */}\n {null} {/* و null أيضًا */}\n</Card>\n\nما الذي يتضمنه ReactNode؟ في الواقع هو اتحاد (Union) من:
\n- \n
ReactElement— عناصر JSX \n stringوnumber— الأنواع البدائية \n booleanوnullوundefined— يتم تجاهلها أثناء العرض \n ReactNode[]— مصفوفة مما سبق \n ReactPortal— البوابات (Portals) \n
2. ReactElement — الوضع الصارم
\n\nإذا كنت بحاجة إلى أن يكون children عنصر JSX فقط (وليس نصًا أو رقمًا)، استخدم ReactElement.
import { ReactElement } from 'react';\n\ninterface StrictCardProps {\n children: ReactElement; // عنصر JSX واحد فقط\n}\n\nfunction StrictCard({ children }: StrictCardProps) {\n return <div className="strict-card">{children}</div>;\n}\n\n// يعمل:\n<StrictCard><span>عنصر فقط</span></StrictCard>\n\n// خطأ من TypeScript:\n<StrictCard>نص</StrictCard> // ❌ النص غير مناسب\n\nمهم: ReactElement يتطلب عنصرًا فرعيًا واحدًا بالضبط. إذا قمت بتمرير عنصرين بدون غلاف، فسيظهر خطأ من TypeScript أيضًا.
3. JSX.Element — اسم مستعار لـ ReactElement
\n\nالنوع JSX.Element هو ببساطة ReactElement<any, any>. إنه أقل مرونة من ReactNode، ولا يُنصح به عمومًا لـ children إلا إذا كنت تكتب مكتبة.
// في الأساس، هما نفس الشيء:\ntype A = JSX.Element; // ReactElement<any, any>\ntype B = ReactElement; // Re