{"id":"l-rf-2-2","courseId":"react-fundamentals","lang":"ar","kind":"lesson","summary":"مرّر البيانات إلى الـ components عبر الـ props، وفكّكها مع قيم افتراضية، واختر بين تمرير الكائنات أو القيم المفردة، وتعامل مع الـ props كلقطات للقراءة فقط.","html":"<p>الـ component المسمّى <code dir=\"ltr\">MovieCard</code> من القسم 1 يقول «Arrival» دائمًا. والـ component الذي لا يعرض إلا فيلمًا واحدًا ليس قابلًا لإعادة الاستخدام؛ إنه قالب له استخدام واحد. الـ props تحلّ هذا: إنها الطريقة التي يسلّم بها الأب البيانات إلى الابن، تمامًا كما تسلّم البيانات إلى دالة عبر وسائطها.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">تمرير الـ props وقراءتها</h2>\n<p>مرّر الـ props كخصائص على وسم الـ component:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">src/App.jsx</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> default</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> App</span><span style=\"color:#953800;--shiki-dark:#FFA657\">() </span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">main</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">MovieCard</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> title</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Arrival\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> year</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2016</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> watched</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> /></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">MovieCard</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> title</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Past Lives\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> year</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2023</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> watched</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">false</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> /></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">main</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  );</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>تجمعها React في كائن واحد، <code dir=\"ltr\">{ title: 'Arrival', year: 2016, watched: true }</code>، وتمرّره كأول وسيط للـ component. وفي الغالب تفكّكه (destructuring) مباشرة في قائمة المعاملات:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">src/components/MovieCard.jsx</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> default</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> MovieCard</span><span style=\"color:#953800;--shiki-dark:#FFA657\">({ title, year, watched }) </span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">article</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">title</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">year</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">watched </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">?</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'Watched'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> :</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'To watch'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">article</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  );</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>ثلاث تفاصيل في الصياغة توقع الناس في الخطأ:</p>\n<ul>\n<li><strong>علامات الاقتباس تعني نصًا.</strong> <code dir=\"ltr\">year=&quot;2016&quot;</code> يمرّر النص “2016”. استخدم الأقواس المعقوصة لأي شيء آخر: <code dir=\"ltr\">year={2016}</code> و<code dir=\"ltr\">watched={false}</code> و<code dir=\"ltr\">genres={['drama', 'sci-fi']}</code>.</li>\n<li><strong>الخاصية المجرّدة تعني <code dir=\"ltr\">true</code>.</strong> <code dir=\"ltr\">&lt;MovieCard watched /&gt;</code> مطابق لـ <code dir=\"ltr\">watched={true}</code>، مثل <code dir=\"ltr\">disabled</code> في HTML.</li>\n<li><strong>التفكيك اختياري.</strong> <code dir=\"ltr\">function MovieCard(props)</code> مع <code dir=\"ltr\">props.title</code> يعمل أيضًا. لكن التفكيك يوثّق بنظرة واحدة الـ props التي يتوقّعها الـ component، ولهذا يستخدمه معظم الكود.</li>\n</ul>\n<h2 id=\"sec-2\" tabindex=\"-1\">القيم الافتراضية</h2>\n<p>امنح الـ prop قيمة افتراضية داخل نمط التفكيك:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">JSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> default</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> MovieCard</span><span style=\"color:#953800;--shiki-dark:#FFA657\">({ title, year, watched </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> false</span><span style=\"color:#953800;--shiki-dark:#FFA657\">, genres </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> [] }) </span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">article</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">title</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">year</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> · </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">genres.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">join</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">', '</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">||</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'No genres yet'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">watched </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">?</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'Watched'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> :</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'To watch'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">article</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  );</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>الآن يعمل <code dir=\"ltr\">&lt;MovieCard title=&quot;Dune&quot; year={2021} /&gt;</code> من دون أن ينهار عند <code dir=\"ltr\">genres.join</code>. تُطبَّق القيم الافتراضية حين يكون الـ prop غائبًا أو <code dir=\"ltr\">undefined</code>، لكن ليس حين يكون <code dir=\"ltr\">null</code>. إذا كانت بياناتك قد تحتوي على <code dir=\"ltr\">null</code>، فتعامل معها صراحةً، باستخدام <code dir=\"ltr\">genres ?? []</code> مثلًا.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">كائنات أم قيم مفردة؟</h2>\n<p>حين يكون الـ component متعلّقًا بشيء واحد، مرّر ذلك الشيء:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">JSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">MovieCard</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> movie</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movie</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> /></span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> MovieCard</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({ </span><span style=\"color:#953800;--shiki-dark:#FFA657\">movie</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movie.title</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>هذا يُبقي أماكن الاستدعاء قصيرة، ويجعل غرض الـ component واضحًا. وسترى أيضًا الـ spread، <code dir=\"ltr\">&lt;MovieCard {...movie} /&gt;</code>، الذي يمرّر كل حقل كـ prop مستقل. إنه مريح، لكنه يُخفي ما يستخدمه الـ component فعلًا، ويمرّر حقولًا لا يحتاجها. استخدمه باعتدال، وعادةً في components تغليف صغيرة تمرّر الـ props إلى عنصر.</p>\n<p>ولتمرير القيم المفردة ميزته أيضًا: الـ component الذي يأخذ <code dir=\"ltr\">title</code> و<code dir=\"ltr\">year</code> يستطيع عرض فيلم أو كتاب أو حلقة بودكاست. اختر بحسب مدى تخصّص الـ component. <code dir=\"ltr\">MovieCard</code> متعلّق بالأفلام، فيكون <code dir=\"ltr\">movie={movie}</code> طبيعيًا. أمّا <code dir=\"ltr\">Card</code> العام فيجب أن يأخذ قيمًا بسيطة.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">الـ props لقطات للقراءة فقط</h2>\n<p>لا يجوز للـ component أبدًا أن يغيّر الـ props الخاصة به. الـ props ملك لـ render الأب: إنها لقطة لما قرّره الأب في تلك اللحظة. وفي وضع التطوير تجمّد React كائن الـ props أصلًا، فيرمي الإسناد إليه خطأ.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>تعديل الـ props لتنسيقها</span></p>\n<p>يبدو <code dir=\"ltr\">props.year = '(' + props.year + ')'</code> غير مؤذٍ، لكنه يكتب في كائن لا يملكه الـ component. اشتقّ قيمة جديدة بدلًا من ذلك: <code dir=\"ltr\">const label = '(' + year + ')'</code>، أو استخدم template literal. القاعدة هي نفسها كما في الدالة النقية: اقرأ مدخلاتك، وأرجع نتيجة، ولا تغيّر شيئًا.</p>\n</aside>\n<p>إذن كيف تتغيّر البطاقة أصلًا؟ يرسمها الأب من جديد بـ props مختلفة. حين يصبح لدى <code dir=\"ltr\">App</code> لاحقًا الأفلام في الـ state ويُعلَّم أحدها كمُشاهَد، يعيد <code dir=\"ltr\">App</code> الـ render ويمرّر <code dir=\"ltr\">watched={true}</code> إلى تلك البطاقة. البطاقة لا تحدّث نفسها؛ بل تُسلَّم بيانات جديدة. هذا التدفّق باتجاه واحد، من الأب إلى الابن، هو ما يجعل تطبيقات React قابلة للتوقّع: لتعرف لماذا تعرض بطاقةٌ شيئًا ما، تنظر إلى الأعلى في الشجرة.</p>\n<h2 id=\"sec-5\" tabindex=\"-1\">تمرير الدوال</h2>\n<p>يمكن للـ props أن تكون أي قيمة، بما فيها الدوال. وهكذا يُخبر الابن أباه بأن شيئًا قد حدث:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">JSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">MovieCard</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> movie</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movie</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> onToggle</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> toggleWatched</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(movie.id)</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> /></span></span></code></pre></div>\n<p>البادئة <code dir=\"ltr\">on</code> عُرف في تسمية الـ callback props، يتماشى مع أحداث الـ DOM مثل <code dir=\"ltr\">onClick</code>. ستستخدم هذا النمط باستمرار في القسم 3، حيث يملك <code dir=\"ltr\">App</code> الـ state ويطلب منه الأبناء تغييره.</p>\n<h2 id=\"sec-6\" tabindex=\"-1\">أصبح <code dir=\"ltr\">ref</code> مجرّد prop</h2>\n<p>أحيانًا يحتاج الأب إلى عقدة الـ DOM الفعلية داخل ابن، مثلًا لنقل التركيز إلى حقل العنوان بعد إضافة فيلم. في React 19 يستقبل الـ function component الـ <code dir=\"ltr\">ref</code> كأي prop آخر ويمرّره إلى عنصر:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"5\"><div class=\"code-head\"><span class=\"code-title\">JSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> TitleInput</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({ </span><span style=\"color:#953800;--shiki-dark:#FFA657\">ref</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">...</span><span style=\"color:#953800;--shiki-dark:#FFA657\">rest</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">input</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> ref</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ref</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> {...</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">rest</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> />;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>الكود الأقدم يغلّف الـ components بـ <code dir=\"ltr\">forwardRef</code> لفعل ذلك. لا تحتاجه في React 19، ومن المتوقّع أن يصبح متوقّفًا (deprecated). لكن هناك prop واحد ما زال مميّزًا: <code dir=\"ltr\">key</code>، الذي ستقابله مع القوائم، تقرأه React ولا يصل أبدًا إلى الـ component الخاص بك.</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">نصيحة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>أبقِ قوائم الـ props قصيرة</span></p>\n<p>إذا كان الـ component يأخذ عشرة props، فهو في الغالب يؤدّي عملين. ابحث عن مجموعة props تنتقل معًا دائمًا، فإمّا أن تمرّرها ككائن واحد وإمّا أن تقسّم الـ component.</p>\n</aside>\n<p>تتيح الـ props للأب أن يضبط الابن بالبيانات. في الدرس التالي سترى كيف تمرّر أجزاء كاملة من JSX إلى component باستخدام <code dir=\"ltr\">children</code>، وكيف يبني ذلك تخطيطات مرنة.</p>\n","toc":[{"id":"sec-1","text":"تمرير الـ props وقراءتها","level":2},{"id":"sec-2","text":"القيم الافتراضية","level":2},{"id":"sec-3","text":"كائنات أم قيم مفردة؟","level":2},{"id":"sec-4","text":"الـ props لقطات للقراءة فقط","level":2},{"id":"sec-5","text":"تمرير الدوال","level":2},{"id":"sec-6","text":"أصبح ref مجرّد prop","level":2}],"takeaways":["الـ props هي وسائط الـ component؛ تمرّرها React ككائن واحد تفكّكه عادةً في قائمة المعاملات.","النصوص يمكن تمريرها بين علامتي اقتباس؛ وكل قيمة أخرى، بما فيها الأرقام والقيم المنطقية، توضع بين أقواس معقوصة.","القيم الافتراضية في التفكيك (<code dir=\"ltr\">watched = false</code>) لا تُطبَّق إلا حين يكون الـ prop غائبًا أو <code dir=\"ltr\">undefined</code>.","لا يجوز للـ component أبدًا أن يغيّر الـ props الخاصة به؛ ولتغيير ما يُعرض، يمرّر الأب props جديدة.","في React 19 أصبح <code dir=\"ltr\">ref</code> مجرّد prop عادي في الـ function components، فلم تعد بحاجة إلى <code dir=\"ltr\">forwardRef</code>."],"further":[{"title":"Passing Props to a Component","url":"https://react.dev/learn/passing-props-to-a-component"},{"title":"Keeping Components Pure","url":"https://react.dev/learn/keeping-components-pure"},{"title":"Manipulating the DOM with Refs (ref as a prop)","url":"https://react.dev/learn/manipulating-the-dom-with-refs"}],"quiz":[{"q":"<p>ما الذي يستقبله <code dir=\"ltr\">MovieCard</code> في <code dir=\"ltr\">year</code> هنا؟ <code dir=\"ltr\">&lt;MovieCard title=&quot;Arrival&quot; year=&quot;2016&quot; /&gt;</code></p>","options":[{"text":"الرقم 2016.","why":"القيم بين علامتي اقتباس نصوص دائمًا في JSX. لتمرير رقم استخدم الأقواس المعقوصة، <code dir=\"ltr\">year={2016}</code>."},{"text":"لا شيء، لأن <code dir=\"ltr\">year</code> يجب أن يُعلَن مسبقًا.","why":"تقبل الـ components أي props تمرّرها؛ لا توجد خطوة إعلان في React مع JavaScript العادية."},{"text":"النص “2016”.","why":"صحيح. علامات الاقتباس تمرّر نصًا حرفيًا. يبدو العرض متطابقًا، لكن <code dir=\"ltr\">year + 1</code> يعطي “20161” بدلًا من 2017."},{"text":"خطأ، لأن الأرقام لا يمكن أن تكون props.","why":"أي قيمة JavaScript يمكن أن تكون prop، بما فيها الأرقام والكائنات والمصفوفات والدوال."}],"answer":2},{"q":"<p>عُرِّف <code dir=\"ltr\">MovieCard</code> بالشكل <code dir=\"ltr\">function MovieCard({ title, watched = false })</code>. ويرسم الأب <code dir=\"ltr\">&lt;MovieCard title=&quot;Dune&quot; watched={null} /&gt;</code>. ما قيمة <code dir=\"ltr\">watched</code> داخل الـ component؟</p>\n","options":[{"text":"<code dir=\"ltr\">null</code>","why":"صحيح. القيم الافتراضية في التفكيك لا تحلّ إلا محلّ <code dir=\"ltr\">undefined</code>. أمّا <code dir=\"ltr\">null</code> الصريحة فتمرّ كما هي."},{"text":"<code dir=\"ltr\">false</code>","why":"كانت القيمة الافتراضية ستُطبَّق لو كان الـ prop غائبًا أو <code dir=\"ltr\">undefined</code>، لكن <code dir=\"ltr\">null</code> قيمة حقيقية."},{"text":"<code dir=\"ltr\">undefined</code>","why":"مرّر الأب <code dir=\"ltr\">null</code>، وReact تمرّر الـ props كما هي."}],"answer":0},{"q":"<p>المقصود من هذا الـ component أن يعرض السنة بالشكل “(2016)”. ما الخطأ فيه؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">JSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> MovieCard</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">props</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  props.year </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> `(${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">props</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">.</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">year</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">})`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">props.title</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> {</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">props.year</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n","options":[{"text":"لا خطأ فيه؛ هذه الطريقة المعتادة لتنسيق الـ props.","why":"الكتابة في الـ props تنقض العقد مع React. كائن الـ props ملك لـ render الأب."},{"text":"الـ template literals غير مسموح بها داخل الـ components.","why":"الـ template literals جزء عادي من JavaScript ومقبولة في أي مكان. المشكلة في الإسناد."},{"text":"يجب أن يستخدم <code dir=\"ltr\">this.props</code> بدلًا من ذلك.","why":"<code dir=\"ltr\">this.props</code> خاص بالـ class components. أمّا الـ function components فتستقبل الـ props كوسيط."},{"text":"إنه يعدّل الـ props. احسب قيمة جديدة بدلًا من ذلك، مثل <code dir=\"ltr\">const label = '(' + year + ')'</code>.","why":"صحيح. الـ props لقطات للقراءة فقط. في وضع التطوير تجمّد React كائن الـ props، فيرمي الإسناد TypeError، وحتى لو لم يفعل، لاختفى التغيير في الـ render التالي."}],"answer":3},{"q":"<p>لديك كائن <code dir=\"ltr\">movie</code> فيه <code dir=\"ltr\">id</code> و<code dir=\"ltr\">title</code> و<code dir=\"ltr\">year</code> و<code dir=\"ltr\">watched</code>. ولا يعرض <code dir=\"ltr\">MovieItem</code> إلا العنوان وحالة المشاهدة. أيّ استدعاء هو الخيار الافتراضي الأوضح؟</p>","options":[{"text":"<code dir=\"ltr\">&lt;MovieItem {...movie} /&gt;</code>، لأن الـ spread أقصر.","why":"الـ spread يُخفي الـ props التي يستخدمها الـ component فعلًا، ويمرّر <code dir=\"ltr\">id</code> و<code dir=\"ltr\">year</code> اللذين لا يحتاجهما. مقبول أحيانًا، لكنه غير واضح كعادة."},{"text":"<code dir=\"ltr\">&lt;MovieItem movie={movie} /&gt;</code>، ويقرأ الـ component القيمتين <code dir=\"ltr\">movie.title</code> و<code dir=\"ltr\">movie.watched</code>.","why":"صحيح. تمرير الكائن كاملًا يُبقي الاستدعاءات قصيرة، وهو خيار افتراضي شائع ومقروء حين يكون الـ component متعلّقًا بذلك الشيء."},{"text":"<code dir=\"ltr\">&lt;MovieItem data={JSON.stringify(movie)} /&gt;</code>","why":"يمكن للـ props أن تكون كائنات. التحويل إلى نص يضيف عملًا ويُضيّع البنية."}],"answer":1}],"exercise":{"kind":"web","mode":"react","starter":{"jsx":"function MovieCard() {\n  return (\n    <article>\n      <h2>Arrival</h2>\n      <p>2016</p>\n      <p>To watch</p>\n    </article>\n  );\n}\n\nexport default function App() {\n  return (\n    <main>\n      <MovieCard />\n      <MovieCard />\n      <MovieCard />\n    </main>\n  );\n}\n"},"solution":{"jsx":"function MovieCard({ title, year, watched = false }) {\n  return (\n    <article>\n      <h2>{title}</h2>\n      <p>{year}</p>\n      <p>{watched ? 'Watched' : 'To watch'}</p>\n    </article>\n  );\n}\n\nexport default function App() {\n  return (\n    <main>\n      <MovieCard title=\"Arrival\" year={2016} watched />\n      <MovieCard title=\"Past Lives\" year={2023} />\n      <MovieCard title=\"Paddington 2\" year={2017} watched={false} />\n    </main>\n  );\n}\n"},"checks":[{"id":"titles","test":"const a = $$('article'); return a.length === 3 && a.map((x) => text(x.querySelector('h2'))).join('|') === 'Arrival|Past Lives|Paddington 2';"},{"id":"years","test":"const a = $$('article'); return a.length === 3 && ['2016', '2023', '2017'].every((y, i) => text(a[i]).includes(y));"},{"id":"watched-status","test":"const a = $$('article'); return a.length === 3 && text(a[0]).includes('Watched') && !text(a[0]).includes('To watch');"},{"id":"default-status","test":"const a = $$('article'); return a.length === 3 && [a[1], a[2]].every((x) => text(x).includes('To watch') && !text(x).includes('Watched'));"}],"title":"اجعل MovieCard قابلًا لإعادة الاستخدام","prompt":"<p>كل البطاقات تقول “Arrival”. امنح <code dir=\"ltr\">MovieCard</code> ثلاثة props، هي <code dir=\"ltr\">title</code> و<code dir=\"ltr\">year</code> و<code dir=\"ltr\">watched</code>، واستخدمها:</p>\n<ul>\n<li>يعرض <code dir=\"ltr\">&lt;h2&gt;</code> العنوان، ويعرض <code dir=\"ltr\">&lt;p&gt;</code> السنة.</li>\n<li>ويعرض <code dir=\"ltr\">&lt;p&gt;</code> آخر النص <strong>Watched</strong> حين تكون <code dir=\"ltr\">watched</code> صحيحة، والنص <strong>To watch</strong> في غير ذلك. اجعل القيمة الافتراضية لـ <code dir=\"ltr\">watched</code> هي <code dir=\"ltr\">false</code>.</li>\n</ul>\n<p>ثم اعرض هذه البطاقات الثلاث في <code dir=\"ltr\">App</code> بهذا الترتيب:</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"جدول قابل للتمرير\" tabindex=\"0\"><table><thead>\n<tr>\n<th>title</th>\n<th>year</th>\n<th>watched</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Arrival</td>\n<td>2016</td>\n<td>true</td>\n</tr>\n<tr>\n<td>Past Lives</td>\n<td>2023</td>\n<td><em>(لا تمرّرها)</em></td>\n</tr>\n<tr>\n<td>Paddington 2</td>\n<td>2017</td>\n<td>false</td>\n</tr>\n</tbody>\n</table></div>","hints":["فكّك الـ props في قائمة المعاملات: <code dir=\"ltr\">function MovieCard({ title, year, watched = false })</code>.","الأرقام والقيم المنطقية توضع بين أقواس معقوصة: <code dir=\"ltr\">year={2016}</code>. والخاصية المجرّدة <code dir=\"ltr\">watched</code> تعني <code dir=\"ltr\">watched={true}</code>.","اختر نص الحالة بالمعامل الثلاثي: <code dir=\"ltr\">{watched ? 'Watched' : 'To watch'}</code>."],"explanation":"<p>صار <code dir=\"ltr\">MovieCard</code> الآن دالة في الـ props الخاصة به. القيمة الافتراضية <code dir=\"ltr\">watched = false</code> تغطّي بطاقة Past Lives التي لا تمرّر هذا الـ prop أصلًا، فتصل <code dir=\"ltr\">watched</code> بقيمة <code dir=\"ltr\">undefined</code> وتُطبَّق القيمة الافتراضية. الأرقام والقيم المنطقية تحتاج إلى أقواس معقوصة؛ أمّا <code dir=\"ltr\">year=&quot;2016&quot;</code> بين علامتي اقتباس فيمرّر نصًا، يبدو متطابقًا هنا لكنه يتصرّف بشكل مختلف في العمليات الحسابية والمقارنات.</p>\n","checkLabels":{"titles":"ثلاث بطاقات بالعناوين Arrival وPast Lives وPaddington 2، بهذا الترتيب","years":"كل بطاقة تعرض سنتها الخاصة","watched-status":"تعرض بطاقة Arrival النص “Watched”","default-status":"تعرض بطاقتا Past Lives وPaddington 2 النص “To watch”"}},"words":662,"readMinutes":4}