{"id":"l-rf-3-1","courseId":"react-fundamentals","lang":"ar","kind":"lesson","summary":"امنح الـ components ذاكرة باستخدام useState، وافهم الـ state كلقطة خاصة بكل render، واستخدم دوال التحديث للتحديثات المتتالية، واستبدل المصفوفات والكائنات بدل تعديلها.","html":"<p>انقر زر «Mark as watched» في function component عادي، ولن يحدث شيء. هذا هو الكود الذي يكتبه معظم الناس أول مرة:</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\">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\"> MovieItem</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\">  let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> watched </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> false</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\"> handleClick</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    watched </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> !</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">watched;</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\">  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\"> onClick</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">={</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">handleClick</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\">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\"> 'Mark as watched'</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></code></pre></div>\n<p>هناك خطآن. تغيير متغيّر محلي لا يُخبر React بأن شيئًا حدث، فلا تعيد الـ render أبدًا. وحتى لو تسبّب شيء آخر في render، فسيُنفَّذ <code dir=\"ltr\">let watched = false</code> مرة أخرى ويعيد القيمة إلى البداية، لأن الـ component دالة، ومتغيّراتها المحلية تبدأ من جديد مع كل استدعاء. يحتاج الـ component إلى شيئين: ذاكرة تبقى بين مرّات الـ render، وطريقة ليطلب من React أن تعيد الـ render. هذا هو الـ state.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">الـ <code dir=\"ltr\">useState</code></h2>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">src/components/MovieItem.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\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { useState } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'react'</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\">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\"> MovieItem</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\">  const</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">watched</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">setWatched</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> useState</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">false</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\">  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\">li</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\">title</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:#0550AE;--shiki-dark:#79C0FF\"> aria-pressed</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:#0550AE;--shiki-dark:#79C0FF\"> onClick</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\"> setWatched</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:#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\">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\"> 'Mark as watched'</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\">    &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">li</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\">useState(false)</code> عن قطعة state واحدة بقيمة ابتدائية <code dir=\"ltr\">false</code>. وتُرجع زوجًا تفكّكه وتسمّيه: القيمة الحالية، ودالة setter (دالة الضبط). التسمية <code dir=\"ltr\">[thing, setThing]</code> عُرف يتّبعه الجميع؛ التزم به.</p>\n<p>حين تستدعي <code dir=\"ltr\">setWatched(true)</code>، تخزّن React القيمة الجديدة وتجدول re-render. فتستدعي <code dir=\"ltr\">MovieItem</code> مرة أخرى، وهذه المرة تُرجع <code dir=\"ltr\">useState</code> القيمة <code dir=\"ltr\">true</code>. يُحسب الـ JSX الخاص بك منها، وتحدّث React الـ DOM ليطابقه.</p>\n<p>الـ state ملك لـ <strong>نسخة (instance)</strong> من الـ component، لا للدالة. ارسم <code dir=\"ltr\">&lt;MovieItem /&gt;</code> ثلاث مرات وستحصل على ثلاث قيم <code dir=\"ltr\">watched</code> مستقلة. النقر على واحدة لا يؤثّر في الأخريين.</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>الدوال التي تبدأ بـ «use» هي hooks</span></p>\n<p>الـ <code dir=\"ltr\">useState</code> هو hook: دالة خاصة تتيح للـ components استخدام ميزات React. تعتمد الـ hooks على أن تُستدعى بالترتيب نفسه في كل render، وبهذا تعرف React أي state هو أي state. لذا استدعِها في المستوى الأعلى من الـ component: لا داخل <code dir=\"ltr\">if</code> ولا داخل حلقة ولا داخل دالة متداخلة. وأداة الفحص في مشروع Vite تتحقّق من قواعد الـ hooks (القاعدة <code dir=\"ltr\">react/rules-of-hooks</code> في Oxlint، أو <code dir=\"ltr\">eslint-plugin-react-hooks</code> إن اخترت ESLint)، وستنبّهك إلى الأخطاء.</p>\n</aside>\n<h2 id=\"sec-2\" tabindex=\"-1\">كل render لقطة</h2>\n<p>هذه هي الفكرة التي تجعل الـ state مفهومًا فجأة. حين تستدعي React الـ component، تسلّمك الـ state <strong>الخاص بذلك الـ render</strong>. وكل ما في ذلك الـ render، الـ JSX ومعالجات الأحداث وكل شيء، يرى تلك القيم. ضبط الـ state لا يغيّر المتغيّر الذي لديك أصلًا؛ بل يطلب render جديدًا بقيمة جديدة.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 220\" role=\"img\" aria-labelledby=\"l-rf-3-1-t1\"><defs><marker id=\"arrow-l-rf-3-1-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-rf-3-1-t1\">الـ render الأول يرى watched تساوي false. نقرة تستدعي setWatched(true)، التي تجدول الـ render الثاني. الـ render الثاني يرى watched تساوي true، وتثبّت React الـ DOM المتغيّر.</title>\n  <rect class=\"d-box\" x=\"20\" y=\"60\" width=\"170\" height=\"80\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"105\" y=\"92\" text-anchor=\"middle\">Render 1</text>\n  <text class=\"d-code\" x=\"105\" y=\"118\" text-anchor=\"middle\">watched = false</text>\n  <rect class=\"d-box-warn\" x=\"265\" y=\"60\" width=\"170\" height=\"80\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"350\" y=\"92\" text-anchor=\"middle\">نقرة</text>\n  <text class=\"d-code\" x=\"350\" y=\"118\" text-anchor=\"middle\">setWatched(true)</text>\n  <rect class=\"d-box-success\" x=\"510\" y=\"60\" width=\"170\" height=\"80\" rx=\"12\"/>\n  <text class=\"d-label-strong\" x=\"595\" y=\"92\" text-anchor=\"middle\">Render 2</text>\n  <text class=\"d-code\" x=\"595\" y=\"118\" text-anchor=\"middle\">watched = true</text>\n  <path class=\"d-arrow\" d=\"M190 100 L263 100\" marker-end=\"url(#arrow-l-rf-3-1-1)\"/>\n  <path class=\"d-arrow\" d=\"M435 100 L508 100\" marker-end=\"url(#arrow-l-rf-3-1-1)\"/>\n  <text class=\"d-label-muted\" x=\"227\" y=\"175\" text-anchor=\"middle\">المستخدم يتصرّف</text>\n  <text class=\"d-label-muted\" x=\"472\" y=\"175\" text-anchor=\"middle\">React تعيد الـ render</text>\n  <text class=\"d-label-muted\" x=\"350\" y=\"205\" text-anchor=\"middle\">لكل render قيمه الخاصة؛ لا شيء يتغيّر في منتصفه</text>\n</svg><figcaption>ضبط الـ state يُطلق render جديدًا بلقطة جديدة</figcaption></figure>\n<p>ولهذا يُفاجأ الناس بهذا:</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\">const</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">count</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">setCount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> useState</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</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\"> addThree</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  setCount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(count </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">); </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// count is 0 → asks for 1</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  setCount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(count </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">); </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// count is still 0 → asks for 1</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  setCount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(count </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">); </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// still 0 → 1</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>الاستدعاءات الثلاثة تقرأ اللقطة نفسها، فتكون النتيجة 1 لا 3. وحين تعتمد القيمة التالية على السابقة، مرّر <strong>دالة تحديث (updater function)</strong> بدلًا من قيمة:</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:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> addThree</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  setCount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">c</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> c </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">); </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// 0 → 1</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  setCount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">c</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> c </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">); </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// 1 → 2</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  setCount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">c</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> c </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">); </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// 2 → 3</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>تضع React دوال التحديث في طابور وتنفّذها بالترتيب، وكل واحدة تستقبل نتيجة سابقتها. لست بحاجة إلى دوال التحديث في كل مكان؛ فـ <code dir=\"ltr\">setWatched(!watched)</code> في معالج نقرة لا بأس به. استخدمها حين تضبط الـ state نفسه أكثر من مرة في معالج واحد، أو في كود يُنفَّذ لاحقًا، مثل مؤقّت.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">المصفوفات والكائنات: استبدل، لا تعدّل</h2>\n<p>يحتفظ Watchlist بمصفوفة من كائنات الأفلام في الـ state. وتقرّر React ما إذا كان الـ state قد تغيّر بمقارنة القيمة القديمة بالجديدة باستخدام <code dir=\"ltr\">Object.is</code>. فإذا غيّرت مصفوفة في مكانها وأعدت المصفوفة نفسها، ترى React المرجع نفسه وقد تتخطّى الـ render كليًا.</p>\n<p>لذا تعامل مع الـ state كقيمة للقراءة فقط، وابنِ قيمًا جديدة:</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:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">movies</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">setMovies</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> useState</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">INITIAL_MOVIES</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// Add: a new array with everything plus one more</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">setMovies</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">([</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">...</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movies, { id: crypto.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">randomUUID</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(), title: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'Dune'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, watched: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">false</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }]);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// Remove: a new array without one item</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">setMovies</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(movies.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">filter</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">m</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> m.id </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">!==</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> id));</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// Update one: a new array, with a new object for the changed item</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">setMovies</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(movies.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">map</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">m</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (m.id </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">===</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> id </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\">m, watched: </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">!</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">m.watched } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> m)));</span></span></code></pre></div>\n<p>هذه الأسطر الثلاثة (الـ spread للإضافة، و<code dir=\"ltr\">filter</code> للحذف، و<code dir=\"ltr\">map</code> مع spread للتحديث) تغطّي معظم تحديثات الـ state التي ستكتبها للقوائم. والكائنات تعمل بالطريقة نفسها: <code dir=\"ltr\">setFilters({ ...filters, year: 2024 })</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>تعديل الـ state مباشرة</span></p>\n<p>الكود <code dir=\"ltr\">movies.push(newMovie); setMovies(movies);</code> يغيّر المصفوفة التي تحملها React ثم يعيدها إليها دون تغيير من وجهة نظر <code dir=\"ltr\">Object.is</code>. لا تتحدّث الشاشة، أو تتحدّث لاحقًا في لحظة عشوائية حين يُطلق شيء آخر re-render. إذا رأيت واجهة قديمة بعد تحديث، فابحث عن <code dir=\"ltr\">push</code> أو <code dir=\"ltr\">splice</code> أو <code dir=\"ltr\">sort</code> أو إسناد مباشر مثل <code dir=\"ltr\">movie.watched = true</code>.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">اختيار القيمة الابتدائية</h2>\n<p>الوسيط الممرَّر إلى <code dir=\"ltr\">useState</code> لا يُستخدم إلا في الـ render الأول، وتتجاهله مرّات الـ render اللاحقة. وإذا كان حساب القيمة الابتدائية مكلفًا، كقراءة بيانات محفوظة وتحليلها، فمرّر دالة بدلًا منها، <code dir=\"ltr\">useState(() =&gt; loadMovies())</code>، فتستدعيها React في الـ render الأول فقط بدل أن تستدعيها في كل render. وستستخدم هذا بالضبط لتحميل Watchlist من localStorage في القسم 4.</p>\n<p>صار بإمكان الـ components أن تتذكّر. في الدرس التالي تنظر عن قرب إلى الأحداث التي تغيّر الـ state: النقر، ولوحة المفاتيح، وكائن الحدث نفسه.</p>\n","toc":[{"id":"sec-1","text":"الـ useState","level":2},{"id":"sec-2","text":"كل render لقطة","level":2},{"id":"sec-3","text":"المصفوفات والكائنات: استبدل، لا تعدّل","level":2},{"id":"sec-4","text":"اختيار القيمة الابتدائية","level":2}],"takeaways":["تُرجع <code dir=\"ltr\">useState</code> القيمة الحالية ودالة setter؛ واستدعاء الـ setter يجدول re-render بالقيمة الجديدة.","كل render يرى لقطة ثابتة من الـ state، لذا فقراءة الـ state مباشرة بعد ضبطه تعطي القيمة القديمة.","استخدم دالة تحديث، <code dir=\"ltr\">setCount((c) =&gt; c + 1)</code>، حين تعتمد القيمة التالية على السابقة.","لا تعدّل الـ state أبدًا؛ أنشئ مصفوفات وكائنات جديدة باستخدام الـ spread و<code dir=\"ltr\">map</code> و<code dir=\"ltr\">filter</code> حتى تلاحظ React التغيير.","استدعِ الـ hooks في المستوى الأعلى من الـ component، ولا تستدعها أبدًا داخل شروط أو حلقات أو دوال متداخلة."],"further":[{"title":"State: A Component's Memory","url":"https://react.dev/learn/state-a-components-memory"},{"title":"State as a Snapshot","url":"https://react.dev/learn/state-as-a-snapshot"},{"title":"Updating Arrays in State","url":"https://react.dev/learn/updating-arrays-in-state"},{"title":"useState reference","url":"https://react.dev/reference/react/useState"}],"quiz":[{"q":"<p>تبدأ <code dir=\"ltr\">count</code> من 0. ماذا يعرض الزر بعد نقرة واحدة؟</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\"> handleClick</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  setCount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(count </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  setCount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(count </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  setCount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(count </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</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":"3","why":"كل استدعاء يقرأ <code dir=\"ltr\">count</code> من الـ render نفسه، حيث قيمتها 0. الاستدعاءات الثلاثة كلها تطلب 0 + 1."},{"text":"0","why":"الـ state يتغيّر فعلًا، لكنه يتغيّر إلى 1، لا إلى 3."},{"text":"خطأ، لأنك لا تستطيع استدعاء الـ setter إلا مرة واحدة في كل معالج.","why":"يمكنك استدعاء الـ setters كما تشاء. تجمعها React وتعيد الـ render مرة واحدة."},{"text":"1","why":"صحيح. <code dir=\"ltr\">count</code> لقطة قيمتها 0 طوال المعالج، فالاستدعاءات الثلاثة كلها تضبط 1. استخدم <code dir=\"ltr\">setCount((c) =&gt; c + 1)</code> لتتراكم التحديثات."}],"answer":3},{"q":"<p>لماذا لا يعمل زر التبديل هذا؟ داخل الـ component يوجد <code dir=\"ltr\">let watched = false;</code>، ومعالج الزر ينفّذ <code dir=\"ltr\">watched = !watched;</code>.</p>","options":[{"text":"لا يمكن قلب القيم المنطقية بـ <code dir=\"ltr\">!</code> في React.","why":"المعامل <code dir=\"ltr\">!</code> يعمل بلا مشكلة. المشكلة في مكان القيمة وفيما يُطلق الـ render."},{"text":"يجب أن يُرجع المعالج القيمة الجديدة.","why":"القيم التي تُرجعها معالجات الأحداث تُتجاهل. لا بدّ من شيء يُخبر React بأن تعيد الـ render."},{"text":"تغيير متغيّر محلي لا يُطلق re-render، والـ render التالي سيعيده إلى false على أي حال.","why":"صحيح. المتغيّرات المحلية لا تبقى بين مرّات الـ render، وReact لا تراقبها. والـ <code dir=\"ltr\">useState</code> يحلّ المشكلتين."},{"text":"يجب أن تكون <code dir=\"ltr\">let</code> هي <code dir=\"ltr\">var</code>.","why":"الكلمة المفتاحية لا تهمّ. أي متغيّر محلي يُعاد إنشاؤه في كل render."}],"answer":2},{"q":"<p>أيّ سطر يعلّم فيلمًا واحدًا كمُشاهَد في الـ state المسمّى <code dir=\"ltr\">movies</code> بشكل صحيح؟</p>","options":[{"text":"<code dir=\"ltr\">setMovies(movies.map((m) =&gt; (m.id === id ? { ...m, watched: true } : m)));</code>","why":"صحيح. تبني <code dir=\"ltr\">map</code> مصفوفة جديدة، والفيلم المتغيّر كائن جديد، والأفلام التي لم تتغيّر يُعاد استخدامها."},{"text":"<code dir=\"ltr\">movies.find((m) =&gt; m.id === id).watched = true; setMovies(movies);</code>","why":"هذا يعدّل الكائن الموجود ويُعيد المصفوفة نفسها. تقارن React باستخدام Object.is، فلا ترى تغييرًا وقد تتخطّى الـ render."},{"text":"<code dir=\"ltr\">setMovies(movies.push({ id, watched: true }));</code>","why":"الدالة <code dir=\"ltr\">push</code> تعدّل المصفوفة وتُرجع الطول الجديد، فيصبح الـ state رقمًا."},{"text":"<code dir=\"ltr\">movies[0].watched = true;</code>","why":"تعديل الـ state لا يُخبر React أبدًا بأن شيئًا تغيّر، وهو يعدّل الفيلم الخطأ إلا إذا صادف أن كان الأول."}],"answer":0},{"q":"<p>داخل معالج نقرة تستدعي <code dir=\"ltr\">setTitle('Dune')</code> ثم <code dir=\"ltr\">console.log(title)</code> في السطر التالي. كان العنوان القديم ‘Arrival’. ماذا يُطبع؟</p>","options":[{"text":"‘Dune’","why":"ضبط الـ state لا يغيّر المتغيّر في الـ render الحالي، بل يطلب من React render جديدًا تكون فيه <code dir=\"ltr\">title</code> هي ‘Dune’."},{"text":"‘Arrival’","why":"صحيح. <code dir=\"ltr\">title</code> ثابت في لقطة هذا الـ render، والقيمة الجديدة تظهر في الـ render التالي."},{"text":"undefined","why":"ما زال المتغيّر يحمل قيمة هذا الـ render، وهي ‘Arrival’."}],"answer":1}],"exercise":{"kind":"web","mode":"react","starter":{"jsx":"import { useState } from 'react';\n\nexport default function App() {\n  let watched = false;\n  const [rating, setRating] = useState(0);\n\n  function toggleWatched() {\n    watched = !watched;\n  }\n\n  function addOne() {\n    setRating(rating + 1);\n  }\n\n  function addTwo() {\n    setRating(rating + 1);\n    setRating(rating + 1);\n  }\n\n  return (\n    <article>\n      <h2>Arrival</h2>\n      <button id=\"toggle\" aria-pressed={watched} onClick={toggleWatched}>\n        {watched ? 'Watched' : 'Mark as watched'}\n      </button>\n      <p id=\"rating\">Rating: {rating}/5</p>\n      <button id=\"plus\" onClick={addOne}>+1</button>\n      <button id=\"plus-two\" onClick={addTwo}>+2</button>\n    </article>\n  );\n}\n"},"solution":{"jsx":"import { useState } from 'react';\n\nexport default function App() {\n  const [watched, setWatched] = useState(false);\n  const [rating, setRating] = useState(0);\n\n  function toggleWatched() {\n    setWatched(!watched);\n  }\n\n  function addOne() {\n    setRating((r) => Math.min(r + 1, 5));\n  }\n\n  function addTwo() {\n    addOne();\n    addOne();\n  }\n\n  return (\n    <article>\n      <h2>Arrival</h2>\n      <button id=\"toggle\" aria-pressed={watched} onClick={toggleWatched}>\n        {watched ? 'Watched' : 'Mark as watched'}\n      </button>\n      <p id=\"rating\">Rating: {rating}/5</p>\n      <button id=\"plus\" onClick={addOne}>+1</button>\n      <button id=\"plus-two\" onClick={addTwo}>+2</button>\n    </article>\n  );\n}\n"},"checks":[{"id":"toggle-on","test":"await click('#toggle'); return text('#toggle') === 'Watched' && $('#toggle').getAttribute('aria-pressed') === 'true';"},{"id":"toggle-off","test":"await click('#toggle'); return text('#toggle') === 'Mark as watched' && $('#toggle').getAttribute('aria-pressed') === 'false';"},{"id":"plus-two","test":"await click('#plus-two'); return text('#rating') === 'Rating: 2/5';"},{"id":"max-five","test":"for (let i = 0; i < 5; i++) await click('#plus'); return text('#rating') === 'Rating: 5/5';"}],"title":"زر المشاهدة والتقييم","prompt":"<p>في بطاقة الفيلم هذه ميزتان معطّلتان.</p>\n<ol>\n<li>زر <strong>Mark as watched</strong> (<code dir=\"ltr\">#toggle</code>) لا يفعل شيئًا. اجعله يبدّل بين <strong>Mark as watched</strong> و<strong>Watched</strong>، مع أن تتبعه قيمة <code dir=\"ltr\">aria-pressed</code>.</li>\n<li>التقييم: الزر <strong>+2</strong> (<code dir=\"ltr\">#plus-two</code>) لا يضيف إلا 1، ويمكن للتقييم أن يتجاوز 5. اجعل +2 يضيف اثنين، ولا تدع التقييم يتجاوز 5 أبدًا، أيًّا كان الزر الذي يُنقر.</li>\n</ol>\n","hints":["متغيّر <code dir=\"ltr\">let</code> يعود إلى قيمته الأولى في كل render ولا يُطلق أي render. خزّن <code dir=\"ltr\">watched</code> باستخدام <code dir=\"ltr\">useState(false)</code>.","الاستدعاءان في <code dir=\"ltr\">addTwo</code> يقرآن لقطة <code dir=\"ltr\">rating</code> نفسها. استخدم دالة تحديث: <code dir=\"ltr\">setRating((r) =&gt; …)</code>.","قيّد القيمة داخل دالة التحديث: <code dir=\"ltr\">setRating((r) =&gt; Math.min(r + 1, 5))</code>. بعدها يمكن لـ <code dir=\"ltr\">addTwo</code> أن تستدعي <code dir=\"ltr\">addOne()</code> مرتين."],"explanation":"<p>يجب أن تكون <code dir=\"ltr\">watched</code> قيمة state: فالمتغيّر المحلي لا يبقى إلى الـ render التالي ولا يتسبّب في حدوثه. أمّا التقييم، فكل معالج يرى لقطة، لذا فإن <code dir=\"ltr\">setRating(rating + 1)</code> مرتين تطلب القيمة نفسها مرتين. أمّا دالة التحديث فتستقبل أحدث قيمة في الطابور، ووضع <code dir=\"ltr\">Math.min(…, 5)</code> بداخلها يطبّق الحدّ على كل تحديث، بما في ذلك نصفا الزر +2.</p>\n","checkLabels":{"toggle-on":"النقر على زر التبديل يعرض “Watched” ويضبط aria-pressed على true","toggle-off":"النقر مجددًا يعرض “Mark as watched” وaria-pressed قيمتها false","plus-two":"الزر +2 يرفع التقييم من 0 إلى 2","max-five":"يتوقّف التقييم عند 5"}},"words":630,"readMinutes":4}