{"id":"l-rf-2-3","courseId":"react-fundamentals","lang":"ar","kind":"lesson","summary":"ابنِ components مرنة باستخدام الـ prop المسمّى children وفتحات JSX، وخصّص الـ components العامة، ومرّر العناصر إلى الأسفل لتتجنّب تمرير الـ props عبر طبقات لا تحتاجها.","html":"<p>سيحتوي Watchlist على عدّة صناديق متشابهة: لوحة «To watch»، ولوحة «Watched»، وربما لوحة «Recommendations» لاحقًا. لكل منها شريط عنوان، وأحيانًا زر أو اثنان على الجانب، ومحتوى مختلف في الداخل. يمكنك إضافة prop لكل نوع محتمل من المحتوى. أو يمكنك أن تدع المستدعي يضع ما يشاء في الداخل، كما تضع أي شيء داخل <code dir=\"ltr\">&lt;div&gt;</code>.</p>\n<p>هذا النهج الثاني هو التركيب (composition)، وبه تُبنى أغلب الـ components المرنة في React.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">الـ prop المسمّى <code dir=\"ltr\">children</code></h2>\n<p>كل ما تضعه بين وسمي component يصل إليه كـ prop اسمه <code dir=\"ltr\">children</code>:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">src/components/Panel.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\"> Panel</span><span style=\"color:#953800;--shiki-dark:#FFA657\">({ title, children }) </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\">section</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> className</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"panel\"</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\">div</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> className</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"panel-body\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">children</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\">div</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\">section</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<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"1\"><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:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Panel</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\">\"To watch\"</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\">MovieList</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> movies</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">toWatch</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\">Panel</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Panel</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\">\"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\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>You've watched </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">watched.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">length</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> movies.&#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\">Panel</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>لا يعرف <code dir=\"ltr\">Panel</code> ما في الداخل ولا يهمّه. هو يملك الإطار (الـ section والعنوان والتنسيق)، والمستدعي يملك المحتوى. لقد استخدمت هذا طوال الوقت من دون أن تلاحظ: <code dir=\"ltr\">&lt;main&gt;</code> و<code dir=\"ltr\">&lt;section&gt;</code> يأخذان children أيضًا.</p>\n<p>يمكن لـ <code dir=\"ltr\">children</code> أن يكون أي شيء قابل للعرض: عنصرًا واحدًا، أو عدّة عناصر، أو نصًا، أو لا شيء. تعامل معه كصندوق مغلق واعرضه في مكانه. لا تفحصه ولا تعدّه ولا تعدّله؛ وإذا احتاج الغلاف إلى معرفة شيء عن محتواه، فيجب أن تصله تلك المعلومة كـ prop عادي.</p>\n<p>تظهر الفائدة حين يتغيّر التصميم. إذا حصلت اللوحات الشهر القادم على زوايا دائرية وظل، فستعدّل <code dir=\"ltr\">Panel</code> مرة واحدة وتتحدّث كل لوحة في التطبيق، مهما كان محتواها.</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>نسيان عرض الـ children</span></p>\n<p>إذا كان <code dir=\"ltr\">Panel</code> يفكّك <code dir=\"ltr\">title</code> فقط ولا يُخرج <code dir=\"ltr\">{children}</code> أبدًا، فسيختفي كل ما بداخله من دون أي خطأ. لا تحذّر React من الـ props غير المستخدمة. حين يختفي محتوى داخل غلاف، تأكّد أن الغلاف يعرض <code dir=\"ltr\">children</code>.</p>\n</aside>\n<h2 id=\"sec-2\" tabindex=\"-1\">أكثر من slot واحد</h2>\n<p>الـ <code dir=\"ltr\">children</code> هو slot (فتحة) واحد. وحين يحتاج الـ component إلى محتوى في عدّة أماكن، استخدم props عادية تحمل JSX. هذه لوحة فيها أزرار اختيارية في ترويستها:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">src/components/Panel.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\"> Panel</span><span style=\"color:#953800;--shiki-dark:#FFA657\">({ title, actions, children }) </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\">section</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> className</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"panel\"</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\">header</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> className</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"panel-header\"</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:#CF222E;--shiki-dark:#FF7B72\">        {</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">actions</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">header</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\">div</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> className</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"panel-body\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">children</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\">div</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\">section</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<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\">Panel</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  title</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"To watch\"</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  actions</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\">button</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> onClick</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">shuffle</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Shuffle&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">button</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</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\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">MovieList</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> movies</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">toWatch</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\">Panel</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>إذا لم يُمرَّر <code dir=\"ltr\">actions</code> فقيمته <code dir=\"ltr\">undefined</code>، وهي لا تعرض شيئًا، فيصبح الـ slot اختياريًا بلا أي جهد. قارن هذا بواجهة مثل <code dir=\"ltr\">buttons={['Shuffle', 'Clear']}</code>: سيحتاج Panel إلى إنشاء الأزرار، وربط المعالجات، والتعامل مع الأيقونات وحالات التعطيل، وإضافة prop جديد مع كل طلب. أمّا مع الـ slot فيقرّر Panel <em>أين</em> توضع الأشياء، ويقرّر المستدعي <em>ما</em> هي.</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>عناصر، لا components</span></p>\n<p>يأخذ الـ slot عنصرًا، <code dir=\"ltr\">actions={&lt;ShuffleButton /&gt;}</code>، لا component، <code dir=\"ltr\">actions={ShuffleButton}</code>. الشكل الثاني يمرّر الدالة نفسها، وReact لا تستطيع عرض دالة كابن.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">التخصيص</h2>\n<p>أحيانًا تريد نسخة ثابتة من component عام. لا تنسخه؛ بل ارسمه مع props مضبوطة مسبقًا:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">src/components/Button.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\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> Button</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({ </span><span style=\"color:#953800;--shiki-dark:#FFA657\">variant</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'neutral'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">children</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\"> (</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">button</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> className</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">`btn btn-${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">variant</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}`</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:#CF222E;--shiki-dark:#FF7B72\">      {</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">children</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">button</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>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">export</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> DangerButton</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:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Button</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> variant</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"danger\"</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> {...</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">props</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>الـ component المسمّى <code dir=\"ltr\">DangerButton</code> هو <code dir=\"ltr\">Button</code> اتُّخذ فيه قرار واحد مسبقًا. والـ <code dir=\"ltr\">...rest</code> في <code dir=\"ltr\">Button</code> يمرّر كل ما عدا ذلك، مثل <code dir=\"ltr\">onClick</code> أو <code dir=\"ltr\">type</code> أو <code dir=\"ltr\">disabled</code>، إلى الـ <code dir=\"ltr\">&lt;button&gt;</code> الحقيقي، فيستخدمه المستدعون كعنصر HTML الذي يعرفونه أصلًا. أصلِح خطأً في <code dir=\"ltr\">Button</code> وستحصل كل الأشكال المشتقّة منه على الإصلاح.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">التركيب بدلًا من الـ prop drilling</h2>\n<p>هذه مشكلة يصادفها كل تطبيق. يملك <code dir=\"ltr\">App</code> الأفلام. ويرسم <code dir=\"ltr\">Layout</code> الـ component المسمّى <code dir=\"ltr\">Sidebar</code>، والشريط الجانبي يعرض <code dir=\"ltr\">WatchCount</code>. لا يحتاج إلى الأفلام إلا <code dir=\"ltr\">WatchCount</code>، لكن لتوصيلها إليه تمرّرها عبر كل مستوى:</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:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Layout</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> movies</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movies</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> />           </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// Layout doesn't use movies…</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> Layout</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({ </span><span style=\"color:#953800;--shiki-dark:#FFA657\">movies</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\">Sidebar</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> movies</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movies</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> />; </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// …neither does Sidebar</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>تمرير البيانات عبر components لا تفعل سوى إعادة تمريرها يُسمّى <strong>prop drilling</strong> (حفر الـ props عبر الطبقات). إنه مملّ، وكل component وسيط صار يعتمد على بيانات لا يستخدمها.</p>\n<p>والتركيب يزيله غالبًا. دع <code dir=\"ltr\">App</code>، الذي يملك البيانات، يُنشئ العنصر، ودع <code dir=\"ltr\">Layout</code> يضعه في مكانه:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"6\"><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\"> 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\">Layout</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> sidebar</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\">WatchCount</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> movies</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movies</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\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">MovieList</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> movies</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movies</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\">Layout</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>\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\"> Layout</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({ </span><span style=\"color:#953800;--shiki-dark:#FFA657\">sidebar</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">children</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\">div</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> className</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"layout\"</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\">aside</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">sidebar</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\">aside</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 style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">children</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\">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\">div</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\">Layout</code> يعرف أن الأفلام موجودة. صار component تخطيط خالصًا يمكنك إعادة استخدامه في أي صفحة. وتوفّر React أيضًا الـ <strong>context</strong> للبيانات التي تحتاجها components كثيرة متباعدة، مثل السمة الحالية أو المستخدم المسجّل دخوله. لكن توثيق React يوصي بتجربة التركيب أولًا، وهو ينجح في الغالب.</p>\n<aside class=\"callout callout-why\" 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\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><circle cx=\"12\" cy=\"12\" r=\"6\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/></svg><span><span class=\"callout-kind\">لماذا يهمّ هذا</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>القطع الصغيرة قطع قابلة للاختبار</span></p>\n<p>الـ <code dir=\"ltr\">Panel</code> الذي لا يفعل سوى ترتيب الـ slots الخاصة به، أو الـ <code dir=\"ltr\">Layout</code> الذي لا يفعل سوى وضع أبنائه، يكاد لا يحتوي على منطق، فلا يكاد يوجد فيه ما ينكسر. يتركّز المنطق في components قليلة تملك البيانات. وهذا التقسيم يجعل الـ components أسهل في الاختبار وإعادة الاستخدام والفهم، وهو مهارة «تركيب شاشات معقّدة من قطع صغيرة» التي وعدت بها هذه الدورة.</p>\n</aside>\n<p>في الدرس التالي تجعل الـ components تعرض أشياء مختلفة بحسب البيانات، من شارة «Watched» إلى رسالة القائمة الفارغة، باستخدام العرض الشرطي.</p>\n","toc":[{"id":"sec-1","text":"الـ prop المسمّى children","level":2},{"id":"sec-2","text":"أكثر من slot واحد","level":2},{"id":"sec-3","text":"التخصيص","level":2},{"id":"sec-4","text":"التركيب بدلًا من الـ prop drilling","level":2}],"takeaways":["كل ما تضعه بين وسمي الفتح والإغلاق لـ component يصل إليه في الـ prop المسمّى <code dir=\"ltr\">children</code>.","يمكن للـ prop أن يحمل JSX، فيستطيع الـ component أن يوفّر عدّة slots مسمّاة مثل <code dir=\"ltr\">actions</code> أو <code dir=\"ltr\">footer</code>.","خصّص الـ component العام برسمه مع props مضبوطة مسبقًا بدلًا من نسخه.","تمرير عناصر جاهزة إلى الأسفل يغنيك غالبًا عن تمرير البيانات عبر components لا تستخدمها."],"further":[{"title":"Passing JSX as children","url":"https://react.dev/learn/passing-props-to-a-component#passing-jsx-as-children"},{"title":"Passing Data Deeply with Context (consider composition first)","url":"https://react.dev/learn/passing-data-deeply-with-context#before-you-use-context"}],"quiz":[{"q":"<p>ما الذي يُعرض داخل <code dir=\"ltr\">&lt;section&gt;</code>؟</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\"> Panel</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({ </span><span style=\"color:#953800;--shiki-dark:#FFA657\">title</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\">section</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\">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\">>&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">section</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>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">Panel</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\">\"To watch\"</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\">MovieList</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> movies</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movies</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\">Panel</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n","options":[{"text":"العنوان ثم قائمة الأفلام.","why":"تُمرَّر القائمة كـ <code dir=\"ltr\">children</code>، لكن Panel لا يعرض <code dir=\"ltr\">children</code> أبدًا، فتسقط بصمت."},{"text":"العنوان فقط؛ القائمة تُمرَّر كـ <code dir=\"ltr\">children</code> لكن Panel لا يعرضها أبدًا.","why":"صحيح. الـ JSX المتداخل يصبح الـ prop المسمّى <code dir=\"ltr\">children</code>، ويجب أن يضع Panel القيمة <code dir=\"ltr\">{children}</code> في مكان ما من مخرجاته."},{"text":"خطأ، لأن Panel لا يعلن عن معامل اسمه <code dir=\"ltr\">children</code>.","why":"لا تشتكي React أبدًا من props غير مستخدمة، ولهذا يسهل أن يفوتك هذا الخطأ."},{"text":"تحلّ قائمة الأفلام محلّ العنوان.","why":"الـ children لا تحلّ أبدًا محلّ مخرجات الـ component نفسه. إنها مجرّد prop آخر قد يعرضه الـ component."}],"answer":1},{"q":"<p>تحتاج إلى <code dir=\"ltr\">Panel</code> له عنوان ومحتوى وأزرار اختيارية في الزاوية العليا. أيّ واجهة استخدام هي الأكثر مرونة ووضوحًا؟</p>","options":[{"text":"prop اسمه <code dir=\"ltr\">buttons</code> يحمل مصفوفة من نصوص التسميات، ويُنشئ Panel الأزرار.","why":"سيضطر Panel إلى معرفة معالجات النقر والأيقونات وحالات التعطيل، وستكبر واجهته بلا نهاية."},{"text":"componentان منفصلان <code dir=\"ltr\">PanelWithButtons</code> و<code dir=\"ltr\">PanelWithoutButtons</code>.","why":"نسخ الـ component لكل شكل (variant) يضاعف الكود الذي تصونه، ومع ذلك لا يغطّي الحالات الجديدة."},{"text":"مرّر الـ markup الكامل للوحة كنص واعرضه باستخدام <code dir=\"ltr\">dangerouslySetInnerHTML</code>.","why":"هذا يتخلّى كليًا عن تهريب React للنصوص وعن الـ components. إنه غير آمن وأصعب كثيرًا في التعديل."},{"text":"prop اسمه <code dir=\"ltr\">actions</code> يأخذ JSX، مثل <code dir=\"ltr\">actions={&lt;button onClick={shuffle}&gt;Shuffle&lt;/button&gt;}</code>.","why":"صحيح. الـ slot من نوع JSX يتيح للمستدعي تمرير أي عناصر بمعالجاتها الخاصة، ولا يقرّر Panel إلا مكانها."}],"answer":3},{"q":"<p>يملك <code dir=\"ltr\">App</code> المصفوفة <code dir=\"ltr\">movies</code>. ويرسم <code dir=\"ltr\">Layout</code> الـ component المسمّى <code dir=\"ltr\">Sidebar</code>، الذي يرسم <code dir=\"ltr\">WatchCount</code>، وهو الوحيد الذي يحتاج إلى <code dir=\"ltr\">movies</code>. ما الحل بالتركيب (composition) لتجنّب تمرير <code dir=\"ltr\">movies</code> عبر Layout وSidebar؟</p>","options":[{"text":"في App، ارسم <code dir=\"ltr\">&lt;Layout sidebar={&lt;WatchCount movies={movies} /&gt;} /&gt;</code>، ودع Layout يضع <code dir=\"ltr\">sidebar</code> في مكانه.","why":"صحيح. يُنشئ App العنصر بالبيانات التي يملكها أصلًا. أمّا Layout وSidebar فيكتفيان بتحديد موضعه ولا يريان <code dir=\"ltr\">movies</code> أبدًا."},{"text":"اجعل <code dir=\"ltr\">movies</code> متغيّرًا عامًا يستورده WatchCount.","why":"البيانات القابلة للتغيير في مستوى الـ module لا تُطلق re-render، وتجعل الـ components صعبة الاختبار وإعادة الاستخدام."},{"text":"انسخ <code dir=\"ltr\">movies</code> إلى state داخل WatchCount.","why":"النسخة الثانية تصبح قديمة حين تتغيّر أفلام App. يجب أن يكون للبيانات مالك واحد."}],"answer":0},{"q":"<p>يأخذ الـ component المسمّى <code dir=\"ltr\">Button</code> الـ prop <code dir=\"ltr\">icon</code> ويعرض <code dir=\"ltr\">&lt;span&gt;{icon}&lt;/span&gt;</code>. أيّ استدعاء يعرض أيقونة نجمة؟</p>","options":[{"text":"<code dir=\"ltr\">&lt;Button icon={StarIcon}&gt;Rate&lt;/Button&gt;</code>","why":"هذا يمرّر دالة الـ component نفسها. تحذّر React من أن الدوال ليست ابنًا صالحًا ولا تعرض شيئًا."},{"text":"<code dir=\"ltr\">&lt;Button icon=&quot;StarIcon&quot;&gt;Rate&lt;/Button&gt;</code>","why":"هذا يمرّر النص “StarIcon”، فيُعرض كهذه الحروف."},{"text":"<code dir=\"ltr\">&lt;Button icon={&lt;StarIcon /&gt;}&gt;Rate&lt;/Button&gt;</code>","why":"صحيح. <code dir=\"ltr\">&lt;StarIcon /&gt;</code> عنصر React يمكن عرضه في أي مكان، والـ slot يتوقّع عناصر."}],"answer":2}],"exercise":{"kind":"web","mode":"react","starter":{"jsx":"function Panel({ title }) {\n  // Add an actions slot in the header and render children in the body\n  return (\n    <section>\n      <header>\n        <h2>{title}</h2>\n      </header>\n    </section>\n  );\n}\n\nexport default function App() {\n  return (\n    <main>\n      <Panel title=\"To watch\" actions={<button>Shuffle</button>}>\n        <ul>\n          <li>Past Lives</li>\n          <li>Paddington 2</li>\n        </ul>\n      </Panel>\n      <Panel title=\"Watched\">\n        <p>You've watched 1 movie.</p>\n      </Panel>\n    </main>\n  );\n}\n"},"solution":{"jsx":"function Panel({ title, actions, children }) {\n  return (\n    <section>\n      <header>\n        <h2>{title}</h2>\n        {actions}\n      </header>\n      <div>{children}</div>\n    </section>\n  );\n}\n\nexport default function App() {\n  return (\n    <main>\n      <Panel title=\"To watch\" actions={<button>Shuffle</button>}>\n        <ul>\n          <li>Past Lives</li>\n          <li>Paddington 2</li>\n        </ul>\n      </Panel>\n      <Panel title=\"Watched\">\n        <p>You've watched 1 movie.</p>\n      </Panel>\n    </main>\n  );\n}\n"},"checks":[{"id":"two-panels","test":"const s = $$('section'); return s.length === 2 && text(s[0].querySelector('header h2')) === 'To watch' && text(s[1].querySelector('header h2')) === 'Watched';"},{"id":"children-first","test":"const s = $$('section'); return s.length === 2 && s[0].querySelectorAll('li').length === 2 && !s[0].querySelector('header li');"},{"id":"children-second","test":"const s = $$('section'); return s.length === 2 && text(s[1]).includes(\"You've watched 1 movie.\") && !s[1].querySelector('header p');"},{"id":"actions-slot","test":"const s = $$('section'); const b = s[0] && s[0].querySelector('header button'); return !!b && text(b) === 'Shuffle';"},{"id":"actions-optional","test":"const s = $$('section'); return s.length === 2 && !s[1].querySelector('button');"}],"title":"Panel فيه slots","prompt":"<p>يستخدم <code dir=\"ltr\">App</code> الـ component المسمّى <code dir=\"ltr\">Panel</code> بالطريقة التي يجب أن يعمل بها، لكن <code dir=\"ltr\">Panel</code> يتجاهل معظم ما يُعطى له. عدّل <strong><code dir=\"ltr\">Panel</code> فقط</strong> بحيث:</p>\n<ul>\n<li>يظهر ما يُمرَّر في <code dir=\"ltr\">actions</code> داخل <code dir=\"ltr\">&lt;header&gt;</code>، بعد <code dir=\"ltr\">&lt;h2&gt;</code>؛</li>\n<li>ويظهر ما يوضع بين وسمي <code dir=\"ltr\">&lt;Panel&gt;</code> (أي <code dir=\"ltr\">children</code>) داخل <code dir=\"ltr\">&lt;section&gt;</code>، <strong>بعد</strong> <code dir=\"ltr\">&lt;header&gt;</code> لا داخله.</li>\n</ul>\n<p>لوحة Watched ليس لها <code dir=\"ltr\">actions</code>، لذا يجب ألّا تعرض أي زر.</p>\n","hints":["فكّك propين إضافيين: <code dir=\"ltr\">function Panel({ title, actions, children })</code>.","اعرض <code dir=\"ltr\">{actions}</code> مباشرة بعد <code dir=\"ltr\">&lt;h2&gt;</code>. وإذا كانت قيمته <code dir=\"ltr\">undefined</code> فلن تعرض React شيئًا.","أضف <code dir=\"ltr\">&lt;div&gt;{children}&lt;/div&gt;</code> بعد وسم الإغلاق <code dir=\"ltr\">&lt;/header&gt;</code>."],"explanation":"<p>يصل الـ JSX المتداخل كـ <code dir=\"ltr\">children</code>، ويحمل الـ prop المسمّى <code dir=\"ltr\">actions</code> عنصرًا، فكلاهما مجرّد قيمة يضعها Panel في الـ markup الخاص به. يقرّر Panel <em>أين</em> توضع الأشياء، ويقرّر <code dir=\"ltr\">App</code> <em>ما</em> هي. ولأن الـ prop الغائب قيمته <code dir=\"ltr\">undefined</code>، و<code dir=\"ltr\">undefined</code> لا تعرض شيئًا، يصبح الـ slot المسمّى actions اختياريًا من دون أي <code dir=\"ltr\">if</code>.</p>\n","checkLabels":{"two-panels":"لوحتان بالعنوانين “To watch” و&quot;Watched&quot;","children-first":"تعرض لوحة To watch عنصري القائمة الخاصين بها، خارج الترويسة","children-second":"تعرض لوحة Watched الفقرة الخاصة بها، خارج الترويسة","actions-slot":"يظهر زر Shuffle داخل ترويسة لوحة To watch","actions-optional":"لوحة Watched لا تحتوي على أي زر"}},"words":636,"readMinutes":4}