EduFlow
أساسيات React
⁦0%⁩

الوحدة 2 · الـ Components والـ Props

الـ props بالتفصيل

21 دقراءة 4 دبيئة تجريب الكود

الدرس في لمحة

مرّر البيانات إلى الـ components عبر الـ props، وفكّكها مع قيم افتراضية، واختر بين تمرير الكائنات أو القيم المفردة، وتعامل مع الـ props كلقطات للقراءة فقط.

  1. 1تمرير الـ props وقراءتها
  2. 2القيم الافتراضية
  3. 3كائنات أم قيم مفردة؟
  4. 4الـ props لقطات للقراءة فقط
  5. 5تمرير الدوال
  6. 6أصبح ref مجرّد prop

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

الـ component المسمّى MovieCard من القسم 1 يقول «Arrival» دائمًا. والـ component الذي لا يعرض إلا فيلمًا واحدًا ليس قابلًا لإعادة الاستخدام؛ إنه قالب له استخدام واحد. الـ props تحلّ هذا: إنها الطريقة التي يسلّم بها الأب البيانات إلى الابن، تمامًا كما تسلّم البيانات إلى دالة عبر وسائطها.

تمرير الـ props وقراءتها

مرّر الـ props كخصائص على وسم الـ component:

src/App.jsx
export default function App() {
  return (
    <main>
      <MovieCard title="Arrival" year={2016} watched />
      <MovieCard title="Past Lives" year={2023} watched={false} />
    </main>
  );
}

تجمعها React في كائن واحد، { title: 'Arrival', year: 2016, watched: true }، وتمرّره كأول وسيط للـ component. وفي الغالب تفكّكه (destructuring) مباشرة في قائمة المعاملات:

src/components/MovieCard.jsx
export default function MovieCard({ title, year, watched }) {
  return (
    <article>
      <h2>{title}</h2>
      <p>{year}</p>
      <p>{watched ? 'Watched' : 'To watch'}</p>
    </article>
  );
}

ثلاث تفاصيل في الصياغة توقع الناس في الخطأ:

  • علامات الاقتباس تعني نصًا. year="2016" يمرّر النص “2016”. استخدم الأقواس المعقوصة لأي شيء آخر: year={2016} وwatched={false} وgenres={['drama', 'sci-fi']}.
  • الخاصية المجرّدة تعني true. <MovieCard watched /> مطابق لـ watched={true}، مثل disabled في HTML.
  • التفكيك اختياري. function MovieCard(props) مع props.title يعمل أيضًا. لكن التفكيك يوثّق بنظرة واحدة الـ props التي يتوقّعها الـ component، ولهذا يستخدمه معظم الكود.

القيم الافتراضية

امنح الـ prop قيمة افتراضية داخل نمط التفكيك:

JSX
export default function MovieCard({ title, year, watched = false, genres = [] }) {
  return (
    <article>
      <h2>{title}</h2>
      <p>{year} · {genres.join(', ') || 'No genres yet'}</p>
      <p>{watched ? 'Watched' : 'To watch'}</p>
    </article>
  );
}

الآن يعمل <MovieCard title="Dune" year={2021} /> من دون أن ينهار عند genres.join. تُطبَّق القيم الافتراضية حين يكون الـ prop غائبًا أو undefined، لكن ليس حين يكون null. إذا كانت بياناتك قد تحتوي على null، فتعامل معها صراحةً، باستخدام genres ?? [] مثلًا.

كائنات أم قيم مفردة؟

حين يكون الـ component متعلّقًا بشيء واحد، مرّر ذلك الشيء:

JSX
<MovieCard movie={movie} />

function MovieCard({ movie }) {
  return <h2>{movie.title}</h2>;
}

هذا يُبقي أماكن الاستدعاء قصيرة، ويجعل غرض الـ component واضحًا. وسترى أيضًا الـ spread، <MovieCard {...movie} />، الذي يمرّر كل حقل كـ prop مستقل. إنه مريح، لكنه يُخفي ما يستخدمه الـ component فعلًا، ويمرّر حقولًا لا يحتاجها. استخدمه باعتدال، وعادةً في components تغليف صغيرة تمرّر الـ props إلى عنصر.

ولتمرير القيم المفردة ميزته أيضًا: الـ component الذي يأخذ title وyear يستطيع عرض فيلم أو كتاب أو حلقة بودكاست. اختر بحسب مدى تخصّص الـ component. MovieCard متعلّق بالأفلام، فيكون movie={movie} طبيعيًا. أمّا Card العام فيجب أن يأخذ قيمًا بسيطة.

الـ props لقطات للقراءة فقط

لا يجوز للـ component أبدًا أن يغيّر الـ props الخاصة به. الـ props ملك لـ render الأب: إنها لقطة لما قرّره الأب في تلك اللحظة. وفي وضع التطوير تجمّد React كائن الـ props أصلًا، فيرمي الإسناد إليه خطأ.

إذن كيف تتغيّر البطاقة أصلًا؟ يرسمها الأب من جديد بـ props مختلفة. حين يصبح لدى App لاحقًا الأفلام في الـ state ويُعلَّم أحدها كمُشاهَد، يعيد App الـ render ويمرّر watched={true} إلى تلك البطاقة. البطاقة لا تحدّث نفسها؛ بل تُسلَّم بيانات جديدة. هذا التدفّق باتجاه واحد، من الأب إلى الابن، هو ما يجعل تطبيقات React قابلة للتوقّع: لتعرف لماذا تعرض بطاقةٌ شيئًا ما، تنظر إلى الأعلى في الشجرة.

تمرير الدوال

يمكن للـ props أن تكون أي قيمة، بما فيها الدوال. وهكذا يُخبر الابن أباه بأن شيئًا قد حدث:

JSX
<MovieCard movie={movie} onToggle={() => toggleWatched(movie.id)} />

البادئة on عُرف في تسمية الـ callback props، يتماشى مع أحداث الـ DOM مثل onClick. ستستخدم هذا النمط باستمرار في القسم 3، حيث يملك App الـ state ويطلب منه الأبناء تغييره.

أصبح ref مجرّد prop

أحيانًا يحتاج الأب إلى عقدة الـ DOM الفعلية داخل ابن، مثلًا لنقل التركيز إلى حقل العنوان بعد إضافة فيلم. في React 19 يستقبل الـ function component الـ ref كأي prop آخر ويمرّره إلى عنصر:

JSX
function TitleInput({ ref, ...rest }) {
  return <input ref={ref} {...rest} />;
}

الكود الأقدم يغلّف الـ components بـ forwardRef لفعل ذلك. لا تحتاجه في React 19، ومن المتوقّع أن يصبح متوقّفًا (deprecated). لكن هناك prop واحد ما زال مميّزًا: key، الذي ستقابله مع القوائم، تقرأه React ولا يصل أبدًا إلى الـ component الخاص بك.

تتيح الـ props للأب أن يضبط الابن بالبيانات. في الدرس التالي سترى كيف تمرّر أجزاء كاملة من JSX إلى component باستخدام children، وكيف يبني ذلك تخطيطات مرنة.

أهم النقاط

  • الـ props هي وسائط الـ component؛ تمرّرها React ككائن واحد تفكّكه عادةً في قائمة المعاملات.
  • النصوص يمكن تمريرها بين علامتي اقتباس؛ وكل قيمة أخرى، بما فيها الأرقام والقيم المنطقية، توضع بين أقواس معقوصة.
  • القيم الافتراضية في التفكيك (watched = false) لا تُطبَّق إلا حين يكون الـ prop غائبًا أو undefined.
  • لا يجوز للـ component أبدًا أن يغيّر الـ props الخاصة به؛ ولتغيير ما يُعرض، يمرّر الأب props جديدة.
  • في React 19 أصبح ref مجرّد prop عادي في الـ function components، فلم تعد بحاجة إلى forwardRef.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

ما الذي يستقبله MovieCard في year هنا؟ <MovieCard title="Arrival" year="2016" />

السؤال 2 من 4

عُرِّف MovieCard بالشكل function MovieCard({ title, watched = false }). ويرسم الأب <MovieCard title="Dune" watched={null} />. ما قيمة watched داخل الـ component؟

السؤال 3 من 4

المقصود من هذا الـ component أن يعرض السنة بالشكل “(2016)”. ما الخطأ فيه؟

JSX
function MovieCard(props) {
  props.year = `(${props.year})`;
  return <p>{props.title} {props.year}</p>;
}
السؤال 4 من 4

لديك كائن movie فيه id وtitle وyear وwatched. ولا يعرض MovieItem إلا العنوان وحالة المشاهدة. أيّ استدعاء هو الخيار الافتراضي الأوضح؟

تدريب

بيئة تجريب الكود

اجعل MovieCard قابلًا لإعادة الاستخدام

كل البطاقات تقول “Arrival”. امنح MovieCard ثلاثة props، هي title وyear وwatched، واستخدمها:

  • يعرض <h2> العنوان، ويعرض <p> السنة.
  • ويعرض <p> آخر النص Watched حين تكون watched صحيحة، والنص To watch في غير ذلك. اجعل القيمة الافتراضية لـ watched هي false.

ثم اعرض هذه البطاقات الثلاث في App بهذا الترتيب:

title year watched
Arrival 2016 true
Past Lives 2023 (لا تمرّرها)
Paddington 2 2017 false
function MovieCard() {
  return (
    <article>
      <h2>Arrival</h2>
      <p>2016</p>
      <p>To watch</p>
    </article>
  );
}

export default function App() {
  return (
    <main>
      <MovieCard />
      <MovieCard />
      <MovieCard />
    </main>
  );
}
جارٍ تحميل المحرّر…

Ctrl/⌘ + Enter للتشغيل · يعمل في بيئة معزولة داخل متصفّحك. لا شيء يغادر جهازك.

المعاينة

الـ console

(لا مخرجات)

الفحوص

  • ثلاث بطاقات بالعناوين Arrival وPast Lives وPaddington 2، بهذا الترتيب
  • كل بطاقة تعرض سنتها الخاصة
  • تعرض بطاقة Arrival النص “Watched”
  • تعرض بطاقتا Past Lives وPaddington 2 النص “To watch”