<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: React</title><link>https://academy.hsoub.com/programming/javascript/react/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: React</description><language>ar</language><item><title>&#x62A;&#x647;&#x64A;&#x626;&#x629; &#x645;&#x634;&#x631;&#x648;&#x639; &#x628;&#x646;&#x627;&#x621; &#x62A;&#x637;&#x628;&#x64A;&#x642; &#x644;&#x625;&#x646;&#x634;&#x627;&#x621; &#x641;&#x648;&#x627;&#x62A;&#x64A;&#x631; &#x628;&#x635;&#x64A;&#x63A;&#x629; PDF &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; React &#x648;Refine</title><link>https://academy.hsoub.com/programming/javascript/react/%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%84%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%81%D9%88%D8%A7%D8%AA%D9%8A%D8%B1-%D8%A8%D8%B5%D9%8A%D8%BA%D8%A9-pdf-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-react-%D9%88refine-r2579/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_05/5.jpg.4ca7342b8b2b47c195ee3676ff0ae29f.jpg" /></p>
<p>
	سنبني في هذا الدليل تطبيقًا لإنشاء الفواتير بصيغة PDF بالاعتماد على مكتبة React، وهي مكتبة لبناء واجهات مستخدم تفاعلية في الويب و <a href="https://github.com/refinedev/refine" rel="external nofollow">إطار عمل Refine</a> الذي يعتمد على رياكت React لتسهيل بناء تطبيقات CRUD وتحسين تجربة المطور في إدارة البيانات والتفاعل مع الواجهات الأمامية، ثم ننشره على منصة <a href="https://www.digitalocean.com/products/app-platform" rel="external nofollow">DigitalOcean’s App Platform</a>.
</p>

<p>
	التطبيق الذي سنبنيه ضمن هذه السلسلة هو أداة مخصصة للشركات الكبيرة لإنشاء فواتير لعملائها. يتضمن جميع الميزات اللازمة للتعامل مع السيناريوهات العملية الواقعية، ويمكن تخصيصه بسهولة ليتناسب مع احتياجاتنا الخاصة.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		ملاحظة: يمكنك الحصول على الشيفرة المصدرية الكاملة للتطبيق الذي سنبنيه في هذا الدليل من هذا <a href="https://github.com/refinedev/refine/tree/master/examples/invoicer" rel="external nofollow">المستودع على GitHub</a>.
	</p>
</blockquote>

<p id="refine">
	سنستخدم التقنيات التالية بجانب Refine:
</p>

<ul>
	<li>
		<a href="https://strapi.io/cloud" rel="external nofollow">نظام إدارة المحتوى Strapi</a> القائم على التخزين السحابي Cloud لتخزين البيانات. ولجلب البيانات منه، سنستخدم <a href="https://refine.dev/docs/data/packages/strapi-v4/" rel="external nofollow">موفر البيانات data-provider المدمج في Refine</a>. يمكننا الوصول إلى <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> الخاص بـ Strapi عبر <a href="https://api.strapi-invoice.refine.dev" rel="external nofollow">api.strapi-invoice</a>
	</li>
	<li>
		مكتبة واجهة المستخدم <a href="https://ant.design/" rel="external nofollow">Ant Design</a>
	</li>
</ul>

<p>
	بعد بناء التطبيق، سننشره باستخدام منصة <a href="https://www.digitalocean.com/" rel="external nofollow">DigitalOcean’s App Platform</a>، التي تُبسّط وتسرّع عملية إعداد وإطلاق وتوسيع التطبيقات والمواقع الثابتة. يمكن نشر الكود عن طريق رابط مستودع GitHub، وستتولى المنصة App Platform إدارة البنية التحتية وبيئات عمل التطبيق والاعتمادات.
</p>

<p id="">
	أما المتطلبات المُسبقة للعمل، فتكون كالآتي:
</p>

<ul>
	<li>
		بيئة تطوير محلية <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">لـ Node.js</a>
	</li>
	<li>
		تحتاج إلى <a href="https://academy.hsoub.com/programming/javascript/react/%D8%A8%D8%AF%D8%A1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-react-r1569/" rel="">معرفة أساسية بـ React</a> وRefine وJSX
	</li>
	<li>
		حساب GitHub لاستضافة الكود البرمجي
	</li>
	<li>
		<a href="https://try.digitalocean.com/developer-cloud/" rel="external nofollow">حساب DigitalOcean</a>
	</li>
</ul>

<h2 id="refine-1">
	ما هو Refine؟
</h2>

<p>
	<a href="https://refine.dev/" rel="external nofollow">إطار عمل Refine</a> هو إطار عمل مفتوح المصدر يعتمد على <a href="https://wiki.hsoub.com/React" rel="external">React</a> لتطوير التطبيقات التي تعتمد على <a href="https://academy.hsoub.com/devops/servers/databases/%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-database/" rel="">إدارة البيانات</a> الكثيرة مثل الأدوات الداخلية، لوحات التحكم Dashboards، ولوحات الإدارة Admin Panels، وجميع أنواع تطبيقات CRUD. يوفر مجموعة من خطافات Hooks والمكونات التي تساهم في تقليل وقت التطوير وتحسين تجربة المطور.
</p>

<p>
	<strong>ملاحظة:</strong> CRUD هي اختصار للعمليات التي تتم على البيانات، وهي اختصار للحرف الأول من كلمات إنشاء Create وقراءة Read وتعديل Update وحذف Delete.
</p>

<p>
	صُمم Refine لبناء تطبيقات الأعمال التجارية لبعضها B2B جاهزة للإنتاج؛ فبدلًا من البدء من الصفر، يوفر إطار العمل بعض الأدوات الأساسية التي تساعد في إدارة البيانات والحالة والاستيثاق Authentication والأذونات.
</p>

<p>
	يتميز Refine باعتماده على بنية بدون رأس Headless، مما يسمح باستخدام أي مكتبة لتصميم واجهة مستخدم أو تصميم ت<a href="https://academy.hsoub.com/programming/css/%D8%AA%D8%B9%D8%B1%D9%91%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-css-r70/" rel="">نسيقات انسيابية CSS</a> خاص. ويدعم مكتبات واجهة المستخدم <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D9%85%D8%B5%D8%B7%D9%84%D8%AD-%D9%85%D9%81%D8%AA%D9%88%D8%AD-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-open-source%D8%9F-r885/" rel="">مفتوحة المصدر </a>مثل Ant Design وMaterial UI و Mantine و Chakra UI. لذا بهذه الطريقة، يمكننا التركيز على بناء الأجزاء المهمة من التطبيق دون الانشغال بالتفاصيل التقنية.
</p>

<h2 id="refine-2">
	إنشاء تطبيق Refine جديد
</h2>

<p>
	ستستخدم الأمر البرمجي التالي لإنشاء المشروع بتفاعلية أكبر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9069_12" style=""><span class="pln">npm create refine</span><span class="pun">-</span><span class="pln">app@latest</span></pre>

<p>
	اختر الخيارات التالية عندما تُطلب:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9069_14" style=""><span class="pun">✔</span><span class="pln"> </span><span class="typ">Choose</span><span class="pln"> a project template </span><span class="pun">·</span><span class="pln"> </span><span class="typ">Vite</span><span class="pln">
</span><span class="pun">✔</span><span class="pln"> </span><span class="typ">What</span><span class="pln"> would you like to name your project</span><span class="pun">?:</span><span class="pln"> </span><span class="pun">·</span><span class="pln"> refine</span><span class="pun">-</span><span class="pln">invoicer
</span><span class="pun">✔</span><span class="pln"> </span><span class="typ">Choose</span><span class="pln"> your backend service to connect</span><span class="pun">:</span><span class="pln"> </span><span class="pun">·</span><span class="pln"> </span><span class="typ">Strapi</span><span class="pln"> v4
</span><span class="pun">✔</span><span class="pln"> </span><span class="typ">Do</span><span class="pln"> you want to use a UI </span><span class="typ">Framework</span><span class="pun">?:</span><span class="pln"> </span><span class="pun">·</span><span class="pln"> </span><span class="typ">Ant</span><span class="pln"> </span><span class="typ">Design</span><span class="pln">
</span><span class="pun">✔</span><span class="pln"> </span><span class="typ">Do</span><span class="pln"> you want to add example pages</span><span class="pun">?:</span><span class="pln"> </span><span class="pun">·</span><span class="pln"> </span><span class="typ">No</span><span class="pln">
</span><span class="pun">✔</span><span class="pln"> </span><span class="typ">Choose</span><span class="pln"> a package manager</span><span class="pun">:</span><span class="pln"> </span><span class="pun">·</span><span class="pln"> npm</span></pre>

<p>
	بعد اكتمال الإنشاء، ننتقل إلى مجلد المشروع ونبدأ التطبيق باستخدام:
</p>

<pre class="ipsCode">npm run dev
</pre>

<p>
	نفتح الرابط <a href="http://localhost:5173" rel="external nofollow">http://localhost:5173</a> في المتصفح لرؤية التطبيق.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpeg" data-fileid="172368" href="https://academy.hsoub.com/uploads/monthly_2025_05/welcome.jpeg.5468e20c0674b62f215993b391742f86.jpeg" rel=""><img alt="welcome" class="ipsImage ipsImage_thumbnailed" data-fileid="172368" data-unique="gqmrv7f6n" src="https://academy.hsoub.com/uploads/monthly_2025_05/welcome.thumb.jpeg.d69497d9814f9b0200622c9101ceaedb.jpeg"> </a>
</p>

<h3 id="-2">
	تثبيت المكتبات الخارجية
</h3>

<p>
	سنحتاج لتثبيت بعض <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-%D9%81%D9%8A-nodejs-r1465/" rel="">حزم <code>npm</code></a> التي سنستخدمها في التطبيق:
</p>

<ul>
	<li>
		<a href="https://github.com/sanniassin/react-input-mask" rel="external nofollow"><code>‏react-input-mask‏‮</code></a>: لتنسيق حقول الإدخال، ستستخدم هذه المكتبة لتنسيق حقل رقم الهاتف
	</li>
	<li>
		؜<a href="https://github.com/ant-design/antd-style" rel="external nofollow"><code>antd-style</code></a>: حل <code>css-in-js</code> خاص بـ Ant Design. ستستخدم هذه المكتبة لتخصيص مكونات Ant Design
	</li>
	<li>
		؜<code>vite-tsconfig-paths</code>: ملحق لـ Vite يسمح بحل الواردات باستخدام مسار JSX
	</li>
</ul>

<p>
	للقيام بذلك ننفذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9069_16" style=""><span class="pln">npm install react</span><span class="pun">-</span><span class="pln">input</span><span class="pun">-</span><span class="pln">mask antd</span><span class="pun">-</span><span class="pln">style</span></pre>

<p>
	ثم نثبت الأنواع التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9069_18" style=""><span class="pln">npm install </span><span class="lit">@types</span><span class="pun">/</span><span class="pln">react</span><span class="pun">-</span><span class="pln">input</span><span class="pun">-</span><span class="pln">mask vite</span><span class="pun">-</span><span class="pln">tsconfig</span><span class="pun">-</span><span class="pln">paths </span><span class="pun">--</span><span class="pln">save</span><span class="pun">-</span><span class="pln">dev</span></pre>

<p>
	بعد تثبيت الحِزم، سنحتاج إلى تحديث ملف <code>tsconfig.json</code> لاستخدام مسار JSX. هذا يجعل استيراد الملفات أسهل للقراءة و الصيانة، فبدلًا من كتابة الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9069_29" style=""><span class="str">"import { Log } from "</span><span class="pun">../../</span><span class="pln">types</span><span class="pun">/</span><span class="typ">Log</span></pre>

<p>
	سنكتب ما يلي: 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9069_27" style=""><span class="str">"import { Log } from "</span><span class="pun">@</span><span class="str">/types/</span><span class="typ">Log</span></pre>

<p>
	لتحقيق ذلك، نضيف الكود التالي إلى ملف <code>tsconfig.json</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9069_20" style=""><span class="pun">{</span><span class="pln">
  </span><span class="str">"compilerOptions"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"target"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"ESNext"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"useDefineForClassFields"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"lib"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"DOM"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"DOM.Iterable"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"ESNext"</span><span class="pun">],</span><span class="pln">
    </span><span class="str">"allowJs"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"skipLibCheck"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"esModuleInterop"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"allowSyntheticDefaultImports"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"strict"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"forceConsistentCasingInFileNames"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"module"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"ESNext"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"moduleResolution"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"bundler"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"resolveJsonModule"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"isolatedModules"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"noEmit"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"jsx"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"react-jsx"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"baseUrl"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"./src"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"paths"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"@/*"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"./*"</span><span class="pun">]</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"include"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"src"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"vite.config.ts"</span><span class="pun">],</span><span class="pln">
  </span><span class="str">"references"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[{</span><span class="pln"> </span><span class="str">"path"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"./tsconfig.node.json"</span><span class="pln"> </span><span class="pun">}]</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنحتاج أيضًا إلى تحديث ملف <code>vite.config.ts</code> لاستخدام ملحق <code>vite-tsconfig-paths</code>، والذي يسمح لـ Vite بحل الواردات باستخدام مسار <code>JSX</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9069_22" style=""><span class="kwd">import</span><span class="pln"> react from </span><span class="str">"@vitejs/plugin-react"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> defineConfig </span><span class="pun">}</span><span class="pln"> from </span><span class="str">"vite"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> tsconfigPaths from </span><span class="str">"vite-tsconfig-paths"</span><span class="pun">;</span><span class="pln">

</span><span class="com">// https://vitejs.dev/config/</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> defineConfig</span><span class="pun">({</span><span class="pln">
  plugins</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">tsconfigPaths</span><span class="pun">({</span><span class="pln"> root</span><span class="pun">:</span><span class="pln"> __dirname </span><span class="pun">}),</span><span class="pln"> react</span><span class="pun">()],</span><span class="pln">
  build</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    rollupOptions</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      output</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        manualChunks</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          antd</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"antd"</span><span class="pun">],</span><span class="pln">
        </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">});</span></pre>

<h3 id="-3">
	إضافة المكونات الأساسية والدوال المساعدة
</h3>

<p>
	لبناء هذا التطبيق، سنحتاج إلى بعض المكونات الأساسية والأنماط والدوال المساعدة من النسخة المكتملة من هذا التطبيق. يوصى بتحميل هذه الملفات من <a href="https://github.com/refinedev/refine/tree/master/examples/invoicer" rel="external nofollow">مستودع GitHub</a> وإضافتها إلى المشروع الذي أنشأناه.
</p>

<p>
	التطبيق فى مستودع GitHub شامل و يعمل جيدًا، لذا سيساعد إعداد هذه الملفات وتجهيزها على اتباع خطوات الدليل وعدم الإطالة فيه.
</p>

<p>
	أولًا، نحذف الملفات والمجلدات التالية من المشروع الذي أنشأته:
</p>

<ul>
	<li>
		مجلد <code>src/components</code>
	</li>
	<li>
		مجلد <code>src/contexts</code>
	</li>
	<li>
		ملف <code>src/authProvider.ts</code>
	</li>
	<li>
		ملف <code>src/constants.ts</code>
	</li>
</ul>

<p>
	ثم ننسخ الملفات والمجلدات التالية إلى نفس الموقع في المشروع:
</p>

<ul>
	<li>
		؜<a href="https://github.com/refinedev/refine/tree/master/examples/invoicer/src/components" rel="external nofollow">components</a>
	</li>
	<li>
		؜<a href="https://github.com/refinedev/refine/tree/master/examples/invoicer/src/providers" rel="external nofollow">providers</a>
	</li>
	<li>
		؜<a href="https://github.com/refinedev/refine/tree/master/examples/invoicer/src/utils" rel="external nofollow">utils</a>
	</li>
	<li>
		؜<a href="https://github.com/refinedev/refine/tree/master/examples/invoicer/src/types" rel="external nofollow">types</a>
	</li>
	<li>
		؜<a href="https://github.com/refinedev/refine/tree/master/examples/invoicer/src/styles" rel="external nofollow">styles</a>
	</li>
</ul>

<h3 id="-4">
	بنية المشروع بعد التعديل
</h3>

<p>
	بعد هذه الخطوات، يجب أن تكون بُنية المشروع كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9069_24" style=""><span class="pun">└──</span><span class="pln"> </span><span class="pun"><span class="ipsEmoji">📁</span></span><span class="pln">src
    </span><span class="pun">└──</span><span class="pln"> </span><span class="pun"><span class="ipsEmoji">📁</span></span><span class="pln">components
    </span><span class="pun">└──</span><span class="pln"> </span><span class="pun"><span class="ipsEmoji">📁</span></span><span class="pln">providers
    </span><span class="pun">└──</span><span class="pln"> </span><span class="pun"><span class="ipsEmoji">📁</span></span><span class="pln">styles
    </span><span class="pun">└──</span><span class="pln"> </span><span class="pun"><span class="ipsEmoji">📁</span></span><span class="pln">types
    </span><span class="pun">└──</span><span class="pln"> </span><span class="pun"><span class="ipsEmoji">📁</span></span><span class="pln">utils
    </span><span class="pun">└──</span><span class="pln"> </span><span class="typ">App</span><span class="pun">.</span><span class="pln">tsx
    </span><span class="pun">└──</span><span class="pln"> index</span><span class="pun">.</span><span class="pln">tsx
    </span><span class="pun">└──</span><span class="pln"> vite</span><span class="pun">-</span><span class="pln">env</span><span class="pun">.</span><span class="pln">d</span><span class="pun">.</span><span class="pln">ts</span></pre>

<p>
	بعد هذه الخطوات، سيُظهِر ملف <code>App.tsx</code> خطأ لأنك أزلت ملفات <code>authProvider.ts</code> و<code>constants.ts</code>. ستقوم بإصلاح ذلك عن طريق تحديث ملف <code>App.tsx</code> في الخطوة التالية.
</p>

<h2 id="-5">
	الخلاصة
</h2>

<p>
	تعرفنا في هذا المقال على أول خطوات تطبيق لإنشاء الفواتير بصيغة PDF باستخدام مكتبة React وإطار Refine. ووضحنا كيفية إعداد المشروع وإعداد البيئة وتثبيت الحزم اللازمة للعمل، ونتابع في لمقال التالي بناء التطبيق ما في ذلك إعداد صفحة تسجيل الدخول ونظام الاستيثاق، إنشاء صفحات لعرض القوائم وتعديل العناصر مثل الحسابات والفواتير، واستيراد وتوجيه هذه الصفحات داخل التطبيق.
</p>

<p>
	ترجمة وبتصرف للمقال <a href="https://www.digitalocean.com/community/developer-center/building-a-react-pdf-invoice-generator-app-with-refine-and-deploying-to-digitalocean-s-app-platform#create-page" rel="external nofollow">Building a React PDF Invoice Generator App with Refine and Deploying it to DigitalOcean's App Platform</a> لكاتبيه Alican Erdurmaz و <a href="https://www.digitalocean.com/community/users/asinghwalia" rel="external nofollow">Anish Singh Walia</a>.
</p>

<h2 id="-6">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%B5%D8%A7%D8%AF%D8%B1-%D8%AA%D8%B9%D9%84%D9%85-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-react-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1575/" rel="" title="أكمل قراءة مصادر تعلم مكتبة React لبناء تطبيقات الويب">مصادر تعلم مكتبة React لبناء تطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D9%85%D8%A7%D9%86-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r2020/" rel="">تعرف على أمان مواقع الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%82%D8%A7%D8%A6%D9%85%D8%A9-%D9%85%D9%87%D8%A7%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-react-r1570/" rel="" title="أكمل قراءة إنشاء تطبيق قائمة مهام باستخدام React">إنشاء تطبيق قائمة مهام باستخدام React</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/git/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9%D9%8A-%D9%84%D9%84%D8%B9%D9%85%D9%84-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D8%BA%D9%8A%D8%AA-git-r1587/" rel="" title="أكمل قراءة الدليل المرجعي للعمل على نظام غيت Git">الدليل المرجعي للعمل على نظام غيت Git</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D8%A7%D9%84%D9%84%D8%A7%D8%B2%D9%85%D8%A9-%D9%84%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-react-r1073/" rel="" title="أكمل قراءة أساسيات جافاسكربت اللازمة للعمل مع React">أساسيات جافاسكربت اللازمة للعمل مع React</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2579</guid><pubDate>Wed, 18 Jun 2025 16:05:02 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x644;&#x645; &#x623;&#x641;&#x636;&#x644; &#x627;&#x633;&#x62A;&#x631;&#x627;&#x62A;&#x64A;&#x62C;&#x64A;&#x627;&#x62A; &#x62A;&#x62D;&#x633;&#x64A;&#x646; &#x645;&#x62D;&#x631;&#x643;&#x627;&#x62A; &#x627;&#x644;&#x628;&#x62D;&#x62B; &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; &#x631;&#x64A;&#x622;&#x643;&#x62A; React</title><link>https://academy.hsoub.com/programming/javascript/react/%D8%AA%D8%B9%D9%84%D9%85-%D8%A3%D9%81%D8%B6%D9%84-%D8%A7%D8%B3%D8%AA%D8%B1%D8%A7%D8%AA%D9%8A%D8%AC%D9%8A%D8%A7%D8%AA-%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D9%85%D8%AD%D8%B1%D9%83%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%B1%D9%8A%D8%A2%D9%83%D8%AA-react-r2333/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_05/---------React.png.04446ff32552c606e609ed652c2a0e2c.png" /></p>
<p>
	تعد ريآكت React أحد أشهر مكتبات لغة جافا سكريبت المستخدمة في تطوير الواجهات الأمامية front-end development، إلا أن تطوير التطبيقات بهذه المكتبة يمكن أن يتسبب لك ببعض المشكلات مع محركات البحث. نستعرض في هذه المقالة أهم التحديات التي تواجه المطورين في تحسين محركات البحث SEO للتطبيقات المصممة باستخدام مكتبة ريآكت React، ونوضح أهم الخطوات التي يمكنك اتباعها لتجاوز هذه المشكلات بفعالية، وتحسين سيو التطبيقات، ورفع رتبتها في صفحة نتائج محرك البحث.
</p>

<h2 id="react-1">
	لمحة عن ريآكت React وآلية عملها
</h2>

<p>
	ريآكت <a href="https://academy.hsoub.com/programming/javascript/react/" rel="">React</a> هي مكتبة جافا سكريبت مخصصة لإنشاء واجهات مستخدم تفاعلية متعددة المنصات وهي تعتمد على <a href="https://academy.hsoub.com/programming/javascript/react/%D8%AA%D9%82%D8%B3%D9%8A%D9%85-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-%D8%A5%D9%84%D9%89-%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-r1571/" rel="">تقسيم التطبيق لعدة مكونات</a> أو أجزاء صغيرة قابلة لإعادة الاستخدام لتساعد في إنشاء تطبيقات واجهات أمامية فعالة وعالية الأداء. طُورت مكتبة ريآكت في الأساس من أجل إنشاء تطبيقات الصفحة الواحدة Single Page Applications أو اختصارًا SPAs -وهي تطبيقات مبنية بالاعتماد على صفحة واحدة ويمكنك أن تنتقل بين صفحاتها دون الحاجة إلى تحميل الصفحات الجديدة بالكامل- ولكنها اليوم تصلح لإنشاء مختلف أنواع مواقع الويب وتطبيقات الجوال. إلا أن المواقع والتطبيقات المصممة بهذه المكتبة قد تواجه مجموعة من التحديات والمشكلات المتعلقة بترتيبها في محركات البحث، وهذا ما ستلاحظه بنفسك إذا كنت قد طورت في البداية تطبيقات ويب بالطريقة التقليدية وكتبت بنفسك كافة التعليمات الأساسية اللازمة لواجهات هذه التطبيقات دون الاستعانة بمكتبات أو<a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">أطر عمل</a> جاهزة، ثم انتقلت بعد ذلك لاستخدام مكتبة ريآكت إذ ستلاحظ أن جزءَا كبيرًا من شيفرات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و <a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> الخاصة بتطبيقك قد أصبحت جزءًا من شيفرات جافا سكريبت <a href="https://wiki.hsoub.com/JavaScript" rel="external">JavaScript</a>. السبب في ذلك هو أن مكتبة <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-react-r1072/" rel="">ريآكت React</a> لا توصي بإنشاء عناصر واجهة المستخدم أو تحديثها بشكل مباشر، بل تعتمد طريقة أخرى بدلاً وهي وصف (حالة state) عناصر واجهة المستخدم أي تصف كيف يجب أن تظهر واجهة المستخدم في أي وقت، ثم تُحدِّث بعد ذلك نموذج تمثيل المستند ككائن <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">DOM</a> ليتوافق مع هذه الحالة بطريقة فعالة، كما توفر ريآكت مكونات قابلة لإعادة الاستخدام يمكننا استخدامها لوصف حالة واجهة المستخدم.
</p>

<p>
	ونتيجة لذلك، سيتوجب عليك إجراء جميع التغييرات على واجهة المستخدم أو على مستند DOM من خلال محرك ريآكت، وبالرغم من أن هذا الأمر مريح للمطورين، إلا أنه من ناحية أخرى يجعل وقت تحميل الصفحات أطول بالنسبة للمستخدمين، كما أنه يتطلب المزيد من العمل في محركات البحث للعثور على المحتوى وفهرسته، الأمر الذي ينجم عنه مشكلات محتملة في ترتيب الصفحات المبنية باستخدام React في نتائج محركات البحث.
</p>

<p>
	سنشرح في الفقرات التالية من المقالة المزيد حول المشكلات التي قد تواجهها في سيو التطبيقات والمواقع التي أنشأتها باستخدام رياكت، ونوفر لك عدة حلول واستراتيجيات للتعامل معها والتخلص منها.
</p>

<h2 id="">
	طريقة زحف محرك البحث جوجل إلى صفحات الويب وفهرستها
</h2>

<p>
	قبل الغوص في حلول تحسين سيو تطبيقات رياكت من الضروري أن تفهم كيف يزحف محرك البحث جوجل Google إلى تطبيقاتك ومواقعك ويفهرسها، فمحرك البحث جوجل هو أشهر محرك بحث ويتلقى ما يزيد على 90% من مجمل عمليات البحث عبر الإنترنت ويمكنك من خلال فهم طريقة عمله معرفة العوامل التي تؤثر على فهرسة موقعك.
</p>

<p>
	للنظر للصورة التالية المقتبسة من <a href="https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics" rel="external nofollow">وثائق Google</a> وتجدر الملاحظة بأن هذا المخطط ليس سوى رسم توضيحي مبسط لعملية فهرسة موقع الويب، فالفهرسة الفعلية التي يقوم بها زاحف جوجل Googlebot أعقد بكثير.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="150218" href="https://academy.hsoub.com/uploads/monthly_2024_05/-----Googlebot.png.c7e6e45cd49a7b88187c06396436a191.png" rel=""><img alt="مخطط فهرسة موقع ويب بواسطة googlebot" class="ipsImage ipsImage_thumbnailed" data-fileid="150218" data-unique="fx4mk1oc6" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_05/-----Googlebot.thumb.png.bebdcb658d7a7cd81ff8f14b7b768a7a.png"> </a>
</p>

<h3 id="-1">
	خطوات فهرسة جوجل
</h3>

<p>
	يتبع زاحف جوجل Googlebot عملية من عدة خطوات لفهرسة مواقع الويب وهي كالتالي:
</p>

<p>
	ينشئ زاحف جوجل رتل انتظار للزحف crawl queue يتضمن كافة عناوين URL التي اكتشفها والتي يحتاج للزحف إليها وفهرستها لاحقًا. عندما يكون الزاحف جاهزًا، فإنه يأخذ عنوان URL التالي في رتل الانتظار، ويرسل طلبًا لجلب محتوى HTML للصفحة. بعدها يقوم الزاحف Googlebot بتحليل محتوى HTML وتحديد فيما إذا كان يحتاج كذلك إلى جلب سكربتات جافا سكريبت وتنفيذها لعرض المحتوى. إذا كانت الإجابة نعم سيضاف عنوان URL إلى رتل انتظار التصيير render queue. لاحقًا، يقوم المصيّر renderer الخاص بالزاحف بجلب شيفرات جافا سكريبت وتنفيذها من أجل تصيير أو عرض render الصفحة ويرسل كود HTML الناتج مرة أخرى إلى وحدة لمعالجة الكود processing unit. تستخرج وحدة المعالجة جميع وسوم الروابط <code>&lt;a&gt;</code> لعناوين URL المكتشفة في صفحة الويب وتضيفها من جديد إلى رتل انتظار الزحف crawl queue. أخيرًا يضاف المحتوى إلى فهرس جوجل.
</p>

<p>
	لاحظ أن هناك اختلافات واضحة بين مرحلة المعالجة التي تحلل كود HTML ومرحلة التصيير التي تنفذ أكواد جافا سكريبت لاحقًا، والسبب في ذلك هو أن تنفيذ أكواد جافا سكريبت مكلف ويستغرق وقتًا طويلاً جدًا، ويحتاج زاحف جوجل للاطلاع على ما يزيد على 130 تريليون صفحة على الويب، لذا عندما يزحف على صفحة ويب فإنه يحلل HTML فورًا ثم يضع أكواد جافا سكريبت في رتل انتظار لتشغيلها لاحقًا، وبحسب وثائق جوجل قد تبقى أكواد جافا سكريبت في رتل انتظار التصيير render queue لمدة بضع ثوانٍ أو أكثر.
</p>

<p>
	ومن الجدير بالذكر أيضًا الإشارة إلى ميزانية الزحف، فعملية زحف وفهرسة جوجل مقيدة بالموارد المتاحة مثل سعة النطاق الترددي والوقت وعدد روبوتات جوجل المتاحة. وتخصص جوجل ميزانية أو موارد محددة لفهرسة كل موقع إلكتروني على شبكة الإنترنت. فإذا كنت تنشئ موقع ويب ضخم يضم آلاف الصفحات والكثير من المحتوى كموقع تجارة إلكترونية على سبيل المثال، وتستخدم في صفحات موقعك قدرًا كبيرًا من أكواد جافا سكريبت لعرض المحتوى، فقد لا يتمكن جوجل من الزحف لكافة صفحات موقعك وقراءة كافة محتوياته لأنه مكلف في الزحف.
</p>

<p>
	ملاحظة: ينصح بقراءة الإرشادات التي يعتمدها جوجل من أجل إدارة وتحسين ميزانية الزحف الخاصة بك من <a href="https://developers.google.com/search/docs/advanced/crawling/large-site-managing-crawl-budget" rel="external nofollow">هنا</a>.
</p>

<h2 id="react-2">
	لماذا يمثل السيو تحديًا في ريآكت React ؟
</h2>

<p>
	بعد أن كونت فكرة عامة عن طريقة عمل زاحف جوجل Googlebot وفهرسته للموقع ستتمكن كمهندس برمجيات من تحديد المشكلات المحتملة التي تواجهها محركات البحث التي تحاول الزحف إلى صفحات مواقع أو تطبيقات ريآكت وفهرستها بشكل أفضل.
</p>

<p>
	وفيما يلي نبين أبرز أسباب حدوث مشكلات في تحسين محركات البحث لمواقع وتطبيقات React ونوضح سبل حلها والتغلب عليها.
</p>

<h3 id="-2">
	السبب الأول: محتوى الصفحة فارغ عند المرور عليها لأول مرة
</h3>

<p>
	تعتمد تطبيقات ريآكت بشكل أساسي على أكواد جافا سكريبت لعرض المحتوى الديناميكي للصفحات، لذا تواجه مشكلات مع محركات البحث في معظم الحالات، حيث تعتمد ريآكت بشكل افتراضي على بنية أو نموذج (هيكل التطبيق App Shell) وهو نموذج لتصميم لتطبيقات الويب يعتمد على إنشاء ملف HTML أساسي عند تحميل الصفحة، ولكن لا يحتوي هذا الملف على أي محتوى ذي معنى، ويجب على المستخدم أو روبوت الزحف تنفيذ أكواد جافا سكريبت لإنشاء ورؤية المحتوى الفعلي للصفحة.
</p>

<p>
	وهذا بدوره يعني أن زاحف جوجل Googlebot سيعثر على صفحة فارغة عند مروره الأول على الصفحة ولن يرى المحتوى الفعلي إلا في مرحلة تصيير أو عرض الصفحة، ونظرًا لكون الزاحف يتعامل مع آلاف الصفحات، سيتسبب هذا الأمر في تأخير فهرسة المحتوى.
</p>

<h3 id="ux">
	السبب الثاني: زيادة وقت التحميل التي تؤثر على تجربة المستخدم UX
</h3>

<p>
	قد يستغرق جلب أكواد جافا سكريبت وتحليلها وتنفيذها وقتًا طويلًا، أضف إلى ذلك فقد تحتاج أكواد جافا سكريبت إلى الاتصال مع شبكة لطلب المحتوى من مصادر مختلفة (مثل قواعد البيانات أو الواجهات البرمجية APIs) وفي هذه الحالة سيضطر المستخدم إلى الانتظار لفترة من الزمن قبل أن يتمكن من الحصول على نتيجة الطلب وعرض المعلومات المطلوبة.
</p>

<p>
	وبما أن جوجل تعتبر أن مواقع الويب التي تستغرق وقتًا طويلاً للتحميل تؤثر سلبًا على <a href="https://academy.hsoub.com/design/user-experience/" rel="">تجربة المستخدم</a> وتعتمد هذا الأمر كمعيار لتحديد ترتيب المواقع في محركات البحث فالمواقع الأبطأ في التحميل ستحصل على ترتيب أقل في صفحة نتائج البحث.
</p>

<h3 id="metadata">
	السبب الثالث: صعوبة تخصيص البيانات الوصفية metadata لكل صفحة من صفحات الموقع
</h3>

<p>
	تفيد الوسوم الوصفية أو ما يعرف بوسوم ميتا <a href="https://wiki.hsoub.com/HTML/meta" rel="external"><meta></a> في وصف صفحات الموقع لمحركات البحث، وتسمح لمحرك بحث جوجل ولمواقع التواصل الاجتماعي بإظهار عناوين وأوصاف هذه الصفحات وصورها المصغرة، لكن المواقع المطورة باستخدام ريآكت تستخدم الوسم
</p>

<p>
	في صفحات الويب المستردة للحصول على هذه المعلومات، وهي لا تنفذ شيفرات جافا سكريبت للصفحة الهدف.
</p>

<p>
	كما تعتمد رياكت على منهجية تصيير كل المحتوى بما في ذلك وسوم ميتا من جانب العميل، وبالتالي سيكون هيكل التطبيق App shell هو نفسه لكامل الموقع أو التطبيق، بمعنى آخر ستكون البيانات الوصفية metadata موحدة لكافة صفحات موقع الويب أو التطبيق وسيكون من الصعب تخصيص هذه البيانات لكل صفحة على حدة، وبهذا سترى محركات البحث نفس البيانات الوصفية لجميع الصفحات، حتى لو كانت هذه الصفحات مختلفة تمامًا عن بعضها البعض وهذا قد يؤثر على فهرستها بصورة صحيحة.
</p>

<h3 id="sitemap">
	السبب الرابع: عدم وجود طريقة مدمجة لإنشاء خريطة الموقع Sitemap
</h3>

<p>
	خريطة الموقع Sitemap هي ملف يوفر معلومات حول محتوى صفحات الموقع ومقاطع الفيديو التي تتضمنها والملفات الأخرى الموجودة على الموقع، ويوضح العلاقات أو الروابط فيما بينها، وتقرأ محركات البحث -ومن بينها محرك جوجل- هذا الملف لتتمكن من الزحف إلى موقعك بطريقة أفضل.
</p>

<p>
	ولا توفر مكتبة ريآكت طريقة مدمجة لإنشاء خريطة الموقع، لكن في حال كنت تستخدم مكتبة React Router للتعامل مع توجيه الصفحات Routing فيمكنك في هذه الحالة العثور على أدوات مساعدة لإنشاء خريطة الموقع، لكن هذا قد يحتاج منك لبعض الجهد.
</p>

<h2 id="-3">
	نصائح عامة لتحسين محركات البحث غير مرتبطة بمكتبة ريآكت
</h2>

<p>
	إليك أهم النصائح والممارسات التي يمكنك الأخذ بها لتحسين محركات البحث للمواقع والتطبيقات بشكل عام وليست خاصة بتطبيقات ريآكت فقط.
</p>

<ul>
	<li>
		<p>
			احصل على عنوان URL سهل لموقعك أو تطبيقك ويمكن فهمه من قبل البشر ومحركات البحث ويعطي فكرة واضحة عن المحتوى الذي يتضمنه.
		</p>
	</li>
	<li>
		<p>
			حَسّن ملف robots.txt لمساعدة روبوتات البحث على فهم كيفية الزحف إلى صفحات موقعك.
		</p>
	</li>
	<li>
		<p>
			استخدم <a href="https://academy.hsoub.com/apps/web/wordpress/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B4%D8%A8%D9%83%D8%A7%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9-%D8%A7%D9%84%D9%85%D8%AD%D8%AA%D9%88%D9%89-cdn-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%A3%D8%AF%D8%A7%D8%A1-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D9%88%D9%88%D8%B1%D8%AF%D8%A8%D8%B1%D9%8A%D8%B3-r299/" rel="">شبكة توصيل محتوى CDN</a> لتخزين جميع الأصول الثابتة مثل CSS و JS والخطوط، واستخدم صورًا متجاوبة لتقليل أوقات تحميل الصفحات.
		</p>
	</li>
</ul>

<p>
	كما يمكن معالجة العديد مشكلات السيو باستخدام التصيير من جانب الخادم SSR أو التصيير المسبق pre-rendering وسنستعرض هذه الأساليب بمزيد من التفصيل في فقرات لاحقة.
</p>

<h3 id="isomorphic">
	ما هي تطبيقات ريآكت المتماثلة Isomorphic
</h3>

<p>
	يشير مصطلح إسومورفيك Isomorphic بشكل عام إلى التوافق أو التشابه في الشكل، أما في مكتبة ريآكت React فهو يعني هذا أن الخادم والعميل لهما له نفس نموذج أو قاعدة الشيفرة تقريبًا، بمعنى آخر يمكن إعادة استخدام نفس مكونات ريآكت React على كل من الخادم والعميل.
</p>

<p>
	وبالتالي يعرف تطبيق رياكت المتماثل Isomorphic بأنه تطبيق يمكن تشغيله على كل من جانب العميل وجانب الخادم باستخدام نفس الشيفرة، إذ يمكن استخدام نفس المكونات على كل منهما ونفس المنطق في التصيير على الخادم والتصيير على العميل وهذا يقلل هذا من تكرار الشيفرة ويضمن تناسق التطبيق.
</p>

<p>
	يسمح هذا النهج المتماثل للخادم يتصيير render تطبيق رياكت وإرسال النتيجة إلى المستخدمين ومحركات البحث ليتمكنوا من عرض المحتوى على الفور، بينما تُحمّل أكواد وتنفذ جافا سكريبت في الخلفية وقد اعتمدت عدة أطر على هذا الأسلوب وزادت من شهرته مثل إطار Next.js ومولد المواقع Gatsby .
</p>

<p>
	تجدر الملاحظة بأن المكونات المتماثلة قد تبدو مختلفة عن مكونات React التقليدية بشكل واضح. على سبيل المثال، يمكن أن تحتوي على كود يعمل على الخادم بدلاً من العميل، كما يمكن أن تشمل حتى مفاتيح سرية وبيانات وصول لواجهات برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> رغم أن كود الخادم يحذف قبل إرساله للعميل.
</p>

<p>
	تسهل هذه الأطر من الكثير من التعقيدات على المطورين وتقدم طريقة محددة لكتابة التعليمات البرمجية. وسنتعمق في تأثيرها على أداء التطبيقات ونوضح العلاقة بين مسارات التصيير render paths وأداء الموقع لاحقًا، لكن لنناقش قبل ذلك بعض المعايير الأساسية لقياس أداء موقع الويب.
</p>

<h2 id="-4">
	مقاييس أداء الموقع
</h2>

<p>
	سنوضح الآن بعض المعايير التي تستخدمها محركات البحث لتصنيف وترتيب مواقع الويب لنساعدك على فهم المزيد حول رفع رتبة موقعك في نتائج البحث.
</p>

<p>
	إلى جانب الإجابة على استفسار المستخدم بسرعة ودقة، تعتقد جوجل أن موقع الويب الجيد يجب أن يحقق ما يلي:
</p>

<ul>
	<li>
		يتمكن المستخدمون من الوصول إلى محتوى الموقع بسرعة ودون الانتظار لوقت طويل.
	</li>
	<li>
		يتفاعل الموقع مع إجراءات المستخدم خلال وقت قصير.
	</li>
	<li>
		يُحمّل الموقع بسرعة.
	</li>
	<li>
		لا يجلب الموقع أي بيانات غير ضرورية، ولا ينفذ تعليمات برمجية مستهلكة للموارد تستنزف بيانات المستخدم أو موارد جهازه.
	</li>
</ul>

<p>
	تتوافق هذه الميزات تقريبًا مع المقاييس التالية:
</p>

<ul>
	<li>
		زمن وصول أول بايت (TTFB): هو اختصار لعبارة Time to First Byte ويعني مقدار الوقت الذي يستغرقه وصول أول جزء من المحتوى من الخادم إلى المتصفح، أو الوقت بين النقر على الرابط ووصول الجزء الأول من المحتوى للمستخدم.
	</li>
	<li>
		أضخم محتوى مرئي (LCP): وهو اختصار لعبارة Largest Contentful Paint ويمثل الزمن الذي يحتاجه عرض العنصر الذي يتضمن أضخم جزء مرئي من الصفحة وهو يعطي فكرة عن زمن انتظار المستخدم لتحميل ما يكفي من الصفحة لتصبح ذات معنى، وتوصي جوجل بأن لا تتجاوز هذه القيمة 2.5 ثانية.
	</li>
	<li>
		زمن التفاعل (TTI): وهو اختصار لعبارة Time to Interactive ويعني الوقت الذي تصبح فيه الصفحة تفاعلية أي يمكن للمستخدم النقر فوق عناصر أو تمريرها…إلخ.
	</li>
	<li>
		حجم الحزمة (Bundle Size): ويمثل إجمالي عدد البايتات المحملة والتعليمات البرمجية المنفذة قبل أن تصبح الصفحة مرئية وتفاعلية بالكامل.
	</li>
</ul>

<p>
	سنعود للحديث عن هذه المقاييس لاحقًا لفهم كيفية تأثير مسارات التصيير render paths المختلفة على كل منها بشكل أفضل. وسنوضح تاليًا ما هي مسارات التصيير المتاحة لمطوري رياكت.
</p>

<h2 id="renderpaths">
	مسارات التصيير Render Paths
</h2>

<p>
	نعني بمسارات التصيير الطرق المختلفة التي يمكن من خلالها الوصول إلى محتوى الصفحة حيث يمكننا تصيير أو عرض تطبيق React على جانب المتصفح client-side أو على جانب الخادم server-side وإنتاج مخرجات مختلفة.
</p>

<p>
	وهنالك وظيفتان تتفاوتان بشكل كبير بين التطبيقات المصيًّرة من جانب العميل والخادم وهما عملية التوجيه routing، وعملية تقسيم التعليمات البرمجية code splitting، لنتعرف على آلية القيام بكل منهما في كل حالة:
</p>

<h3>
	حالة التصيير من جانب العميل (CSR)
</h3>

<p>
	التصيير من جانب العميل هو مسار التصيير الافتراضي في تطبيقات رياكت ذات الصفحة الواحدة React SPA. حيث أن الخادم يوفر غلاف تطبيق shell app لا يتضمن أي محتوى فعلي وبمجرد قيام المتصفح بتحميل أكواد جافا سكريبت JavaScript المضمنة في الصفحة ويحللها وينفذها سيملأ محتوى HTML ويعرض بالكامل.
</p>

<p>
	في هذه الحالة يتم التعامل مع وظيفة التوجيه بواسطة تطبيق العميل client app من خلال إدارة سجل المتصفح browser history. وهذا يعني أنه يتم تصيير أو عرض نفس ملف HTML بغض النظر عن المسار الذي طُلِب، ويقوم العميل بتحديث حالة العرض view state الخاصة به بعد عرضه.
</p>

<p>
	أما تقسيم الكود في هذه الحالة فهو أمر بسيط نسبيًا حيث يمكن تقسيم التعليمات البرمجية الخاصة بالتطبيق باستخدام الاستيراد الديناميكي dynamic imports أو بالاستعانة بمكتبة React.lazy إذ يقسم الكود بحيث يتم تحميل التبعيات المطلوبة فقط بناءً على المسار أو إجراءات المستخدم.
</p>

<p>
	إذا كانت الصفحة بحاجة إلى جلب بيانات معينة من الخادم لتصيير المحتوى مثل عنوان المدونة أو وصف منتج ما فلن تتمكن من القيام بذلك إلا عندما يتم تثبيت المكونات ذات الصلة وعرضها، وسيرى المستخدم غالبًا عبارة مثل "جاري تحميل البيانات" أثناء قيام موقع الويب بجلب واستيراد الموقع لبيانات إضافية.
</p>

<p>
	إذَا في هذه الحالة لن تسترد البيانات من الخادم إلا عند الحاجة إليها لعرض المحتوى. وهذا يقلل وقت التحميل الأولي للصفحة ويوفر تجربة مستخدم أفضل. ومع ذلك، قد يرى المستخدم مؤشر تحميل أثناء استرداد البيانات الإضافية.
</p>

<h3 id="csrb">
	التصيير من جانب العميل باستخدام البيانات التمهيدية (CSRB)
</h3>

<p>
	لنفترض أن لدينا حالة تصيير من جانب العميل CSR ولكن بدلاً من جلب البيانات بعد تصيير وعرض مستند DOM بحيث تظهر المحتويات أو البيانات ذات الصلة بشكل تدريجي، سنفترض أن الخادم يرسل البيانات ذات الصلة مباشرة داخل كود HTML المُصَيَّر.
</p>

<p>
	يمكن أن نضيف عقدة node كما يلي داخل صفحة HTML:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5805_7" style=""><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"data"</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"application/json"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="pun">{</span><span class="str">"title"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"My blog title"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"comments"</span><span class="pun">:[</span><span class="str">"comment 1"</span><span class="pun">,</span><span class="str">"comment 2"</span><span class="pun">]}</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	يضمن هذا الكود البيانات بصيغة JSON داخل صفحة HTML، وتحلل هذه القاعدة التي تحتوي على البيانات المطلوبة (وهي عنوان المدونة والتعليقات عليها) وتفسر عند إضافة المكون إلى مستند DOM على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5805_9" style=""><span class="kwd">var</span><span class="pln"> data </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">).</span><span class="pln">innerHTML</span><span class="pun">);</span></pre>

<p>
	بهذا نكون قد وفرنا عملية ذهاب وعودة من الخادم، وسيظهر المحتوى بشكل أسرع للمستخدم لأن المتصفح لم يضطر إلى الانتظار لطلب البيانات من الخادم مرة أخرى.
</p>

<h3 id="ssrs">
	تصيير المحتوى الثابت من جانب الخادم (SSRS)
</h3>

<p>
	في هذه الحالة يتم إنشاء وتصيير الصفحة بالكامل على جانب الخادم قبل إرساله إلى المتصفح، تخيل على سبيل المثال حالة لموقع أو تطبيق ويب تحتاج فيه إلى إنشاء كود <a href="https://academy.hsoub.com/programming/html/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-html-r1702/" rel="">HTML</a> بسرعة، على سبيل المثال إذا كنت تريد بناء تطبيق ريآكت لآلة حاسبة عبر الإنترنت، وأدخل المستخدم لهذا التطبيق استعلام من المستخدم من خلال عنوان URL على النحو التالي:
</p>

<pre class="ipsCode">/calculate/34+15
</pre>

<p>
	يحتاج التطبيق لمعالجة هذا الاستعلام وتقييم النتيجة والرد بكود HTML الذي تم إنشاؤها، وبما أن كود HTML النتاتج يملك بنية بسيط جدًا فلست بحاجة لأن تقوم رياكت بإدارة DOM ومعالجته ويمكنك الاكتفاء بعرض HTML الناتج مباشرة.
</p>

<p>
	لذلك يمكن توفير محتوى HTML و CSS فقط واستخدام التابع <code>renderToStaticMarkup</code> لتحقيق ذلك.
</p>

<p>
	سيتعامل التطبيق في هذه الحالة مع التوجيه بالكامل من جانب الخادم لأنه يحتاج إلى إعادة حساب HTML لكل نتيجة (كلما تم تغيير عنوان URL)، كما يمكن استخدام التخزين المؤقت لكود HTML الناتج ضمن شبكة توصيل المحتوى CDN لتسريع استجابة التطبيق. ويمكن أيضًا تخزين ملفات CSS مؤقتًا بواسطة المتصفح لتحميل الصفحات اللاحقة بسرعة أكبر.
</p>

<h3 id="ssrh">
	التصيير من جانب الخادم مع المكونات المعززة (SSRH)
</h3>

<p>
	المكونات المشبعة أو المعززة Hydrated Components هي مكونات رياكت تعمل كجسر بين نسخ التطبيق التي تنشأ على الخادم والعميل، ولفهم آلية عملها تخيل الآن أنك تحتاج لتطوير تطبيق رياكت كامل الوظائف على العميل، أي تطبيق يتطلب تفاعل المستخدمين ويوفر إمكانية تنقلهم بين الصفحات ويحتاج لجلب البيانات من الخادم. ملاحظة:يشير مصطلح التشبيع أو التعزيز hydration في رياكت إلى الطريقة التي تتصل بها React مع كود HTML الموجود مسبقًا والذي تم تصييره بالفعل بواسطة رياكت في جانب الخادم. فهو عملية يتم فيها ربط أو تعزيز شيفرة جافا سكريبت التفاعلية لمحتوى HTML الثابت المصير مسبقًا على الخادم لجعل كود HTML تفاعليًا. وبالتالي تقوم رياكت بتنفيذ التصيير الأول لصفحات الموقع على الخادم، وترسل محتوى HTML الناتج إضافة إلى ملفات جافا سكريبت إلى العميل، ثم تقوم بإعادة تعزيز الكود المرسل من جانب الخادم، ليصبح التطبيق وكأنه تطبيق تصيير من جانب العميل (CSR) من هذه النقطة فصاعدًا.
</p>

<p>
	توفر رياكت عدة طرق مدمجة لتنفيذ هذه الإجراءات تسمى ReactDOMServer، حيث تعالج رياكت أول طلب لعرض أو تصيير صفحة ما من جانب الخادم، ثم تتعامل مع الطلبات التالية لعرض هذه الصفحة من جانب العميل. ولهذا يطلق على هذه التطبيقات اسم تطبيقات رياكت العامة لكون عملية العرض أو التصيير تتم على جانبي الخادم والعميل كليهما، وقد يتم تقسيم (أو تكرار) الكود الذي يتعامل مع التوجيه على كل من الخادم والعميل.
</p>

<p>
	إن تقسيم الكود أمر صعب بعض الشيء أيضًا، لأن توابع <code>ReactDOMServer</code> المستخدمة لإنشاء صفحات HTML ثابتة على الخادم لا تدعم React.lazy، لذلك لا يمكن استخدامها لتحميل المكونات ديناميكيًا على الخادم وللتعامل مع هذا الأمر قد تضطر لاستخدام المكونات القابلة للتحميل وهي مكونات يمكنك تحميلها ديناميكيًا عند الحاجة. وقد تضطر إلى استخدام شيء مثل Loadable Components.
</p>

<p>
	يجب التنويه أيضًا أن <code>ReactDOMServer</code> تقوم فقط بعرض بسيط. وبالرغم من استدعاء طريقة التصيير لمكوناتك، إلا أن الطريقة <code>componentDidMount</code> التي ستحتاج لاستدعائها بعد أن يتم تثبيت المكون في DOM لا تستدعى في هذه الحالة. لذلك تحتاج إلى إعادة تصميم كودك لتوفر البيانات لمكونات التطبيق باستخدام طريقة بديلة.
</p>

<p>
	هنا تبرز فائدة إطارات العمل مثل NextJS. فهي تختفي التعقيدات المرتبطة بعمليات التوجيه وتقسيم الكود في حالة تصيير الصفحات على الخادم وتعتمد أسلوب التصيير من جانب الخادم مع المكونات المعززة SSRH لتوفر تجربة أكثر سلاسة للمطورين.
</p>

<h3 id="prs">
	التصيير المسبق للمحتوى الثابت (PRS)
</h3>

<p>
	ماذا لو تمكنا من عرض صفحات الموقع قبل أن يطلبها المستخدم؟ يسمى هذا الأمر التصيير المسبق Pre-rendering وهو أسلوب لإنشاء محتوى HTML للتطبيق بالكامل على الخادم قبل أن يطلبه المستخدم، بعدها يرسل المحتوى للمتصفح ويعرض مباشرة دون الحاجة إلى إنشاء واجهة المستخدم بالكامل من البداية، ويمكن القيام بذلك في وقت إنشاء الموقع أو ديناميكيًا عندما تتغير البيانات.
</p>

<p>
	ويمكن فيما بعد تخزين محتوى HTML الناتج على شبكة توصيل المحتوى CDN وتصييره بشكل أسرع عندما يطلبه المستخدم وهو أمر مفيد في كافة المواقع التي لا يعتمد محتواها على البيانات المقدمة من قبل المستخدم كالمدونات وتطبيقات التجارة الإلكترونية.
</p>

<h3 id="prh">
	التصيير المسبق مع تفعيل المكونات (PRH)
</h3>

<p>
	قد ترغب في أن يكون HTML المصير مسبقًا تطبيق React يعمل بكامل طاقته عندما يعرضه العميل. في هذه الطريقة، ينشأ محتوى HTML للتطبيق بالكامل على الخادم كما هو الحال في التصيير المسبق العادي، وإلى جانب ذلك تضمن كذلك معلومات الحالة المقابلة لكل عنصر رياكت في محتوى HTML. وبالتالي في هذه الطريقة، بعد تقديم الطلب الأول لعرض التطبيق، سيتصرف التطبيق مثل تطبيق React القياسي. وبشكل مشابه لحالة تصيير الصفحات على الخادم SSRH من حيث التعامل مع وظائف التوجيه وتقسيم التعليمات البرمجية.
</p>

<h2 id="-5">
	مصفوفة الأداء
</h2>

<p>
	لنناقش الآن كيفية تأثير كل مسار من مسارات التصيير التي وضحناها سابقًا على مقاييس أداء مواقع الويب ونعرف أي طريقة منها هي الأفضل من خلال الجدول التالي الذي يعطي نتيجة تتراوح من 1 إلى 5 وتدل النتائج على ما يلي:
</p>

<ol>
	<li>
		نتيجة غير مرضية
	</li>
	<li>
		نتيجة ضعيفة 3.نتيجة متوسطة
	</li>
	<li>
		نتيجة جيدة
	</li>
	<li>
		نتيجة ممتازة
	</li>
</ol>

<table>
	<thead>
		<tr>
			<th>
				 
			</th>
			<th>
				TTFB
			</th>
			<th>
				LCP
			</th>
			<th>
				TTI
			</th>
			<th>
				حجم الحزمة
			</th>
			<th>
				المجموع
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<strong>CSR</strong>
			</td>
			<td>
				5<br>
				يمكن تخزين HTML مؤقتاً على شبكة CDN
			</td>
			<td>
				1<br>
				يحتاج للذهاب مرات متعددة للخادم لجلب البيانات
			</td>
			<td>
				2<br>
				تأخر في جلب البيانات وفي تنفيذ أكواد جافا سكريبت
			</td>
			<td>
				2<br>
				تحميل جميع تبعيات الجافا سكريبت قبل التصيير
			</td>
			<td>
				10
			</td>
		</tr>
		<tr>
			<td>
				<strong>CSRB</strong>
			</td>
			<td>
				4<br>
				يمكن تخزين HTML مؤقتًا بشرط عدم اعتماده على بيانات الطلب
			</td>
			<td>
				3<br>
				تحميل البيانات مع التطبيق
			</td>
			<td>
				3<br>
				يجب جلب جافا سكريبت وتحليلها وتنفيذها قبل التفاعل
			</td>
			<td>
				2<br>
				تحميل جميع تبعيات الجافا سكريبت قبل التصيير
			</td>
			<td>
				12
			</td>
		</tr>
		<tr>
			<td>
				<strong>SSRS</strong>
			</td>
			<td>
				3<br>
				يُولَّد كود HTML في كل طلب ولا يمكن تخزينه مؤقتًا
			</td>
			<td>
				5<br>
				عدم تحميل جافا سكريبت أو عمليات غير متزامنة
			</td>
			<td>
				5<br>
				الصفحة تكون تفاعلية فورًا بعد الرسم الأول
			</td>
			<td>
				5<br>
				يحتوي على المحتوى الثابت الأساسي فقط\
			</td>
			<td>
				18
			</td>
		</tr>
		<tr>
			<td>
				<strong>SSRH</strong>
			</td>
			<td>
				3<br>
				يُولَّد HTML في كل طلب ولا يمكن تخزينه مؤقتًا
			</td>
			<td>
				4<br>
				الرسم الأول أسرع بسبب التصيير الأول على جانب الخادم
			</td>
			<td>
				2<br>
				أبطأ بسبب تنشيط DOM بعد تحليل ورسم HTML أول مرة
			</td>
			<td>
				1<br>
				HTML الناتج+ تحميل تبعيات الجافا سكريبت
			</td>
			<td>
				10
			</td>
		</tr>
		<tr>
			<td>
				<strong>PRS</strong>
			</td>
			<td>
				5<br>
				تخزين HTML مؤقتًا على شبكة CDN
			</td>
			<td>
				5<br>
				عدم تحميل جافا سكريبت أو عمليات غير متزامنة
			</td>
			<td>
				5<br>
				تكون الصفحة تكون تفاعلية فورًا بعد الرسم الأول
			</td>
			<td>
				5<br>
				يحتوي على المحتوى الثابت الأساسي فقط
			</td>
			<td>
				20
			</td>
		</tr>
		<tr>
			<td>
				<strong>PRH</strong>
			</td>
			<td>
				5<br>
				تخزين HTML مؤقتًا على شبكة CDN
			</td>
			<td>
				4<br>
				التصيير الأول أسرع بسبب التصيير من جانب الخادم لأول مرة
			</td>
			<td>
				2<br>
				أبطأ بسبب تفعيل DOM بعد تحليل ورسم HTML الأول
			</td>
			<td>
				1 HTML المصير+ تحميل تبعيات الجافا سكريبت
			</td>
			<td>
				12
			</td>
		</tr>
	</tbody>
</table>

<p>
	يمكن أن نستنتج من الجدول السابق ما يلي:
</p>

<ul>
	<li>
		يؤدي التصيير المسبق للمحتوى الثابت (PRS) إلى أفضل أداء لمواقع الويب، بينما قد يؤدي التصيير من جانب الخادم مع المكونات المعززة (SSRH) أو التصيير من جانب العميل (CSR) إلى نتائج غير مرضية.
	</li>
	<li>
		من الممكن أيضًا استخدام أساليب متعددة لأجزاء مختلفة من موقع الويب. على سبيل المثال، قد تكون مقاييس الأداء هذه مهمة لصفحات الويب العامة حتى يمكن فهرستها بشكل أكثر فعالية، بينما قد تكون أقل أهمية بمجرد دخول المستخدم ورؤية بيانات حسابه الخاصة.
	</li>
	<li>
		يعتمد اختيار كل مسار على طبعة تطبيقك وكيفية معالجة بياناتك، اختر ما يناسبك بحيث تحسن تجربة المستخدمين بأفضل صورة ممكنة.
	</li>
</ul>

<h2 id="-6">
	طرق إضافية لتحسين سيو مواقع الويب
</h2>

<p>
	حاولنا في هذه المقالة أن نناقش الأساليب والتقنيات الشائعة حاليًا بتحسين أداء تطبيقات رياكت وتعزيزها في محركات البحث، ويمكن إضافة إلى ما سبق الاطلاع على النصائح والاستراتيجيات التي يوفرها مطورو جوجل.
</p>

<p>
	وأخيرًا من العوامل الإضافية التي تساعدك على تحسين سيو تطبيقات ومواقع الويب المطورة بمكتبة رياكت اعتماد نظام إدارة محتوى موقعك CMS جيد لاسيما عند إنشاء مواقع ويب ذات محتوى ضخم، وتوفير ميزة إنشاء وتعديل معاينات وسائل التواصل الاجتماعي بسهولة وتحسين الصور لتتناسب مع مختلف أحجام الشاشات فهذه العوامل من شأنها تحسين سرعة تحميل الصفحة وتحسين تجربة المستخدم بشكل كبير.
</p>

<h2 id="-7">
	الخاتمة
</h2>

<p>
	هذا تكون قد وصلت لنهاية هذا المقال الذي وضحنا فيه أبرز تحديات تحسين محركات البحث في تطبيقات ومواقع رياكت، ووضحنا أهم الاستراتيجيات التقنيات المستخدمة التي يمكنك القيام بها للتغلب عليها لتحسين سيو تطبيقات ومواقع رياكت React الخاصة بك وتحسين فرصك في ظهورها في نتائج البحث.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%B4%D8%A7%D9%85%D9%84%D8%A9-%D8%A8%D9%8A%D9%86-%D8%B1%D9%8A%D8%A7%D9%83%D8%AA-react-%D9%88-nextjs-r2326/" rel="">مقارنة شاملة بين رياكت React و Next.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A7%D9%84%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-react-r774/" rel="">المصطلحات المستخدمة في React</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%B4%D9%85%D9%88%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-react-%D9%84%D9%83%D8%A7%D9%81%D8%A9-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D9%8A%D9%86-r1574/" rel="">شمولية تطبيقات React لكافة أنواع المستخدمين</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%AA%D9%82%D8%B3%D9%8A%D9%85-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-%D8%A5%D9%84%D9%89-%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-r1571/" rel="">تقسيم تطبيق React إلى مكونات</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2333</guid><pubDate>Tue, 28 May 2024 12:04:03 +0000</pubDate></item><item><title>&#x645;&#x642;&#x627;&#x631;&#x646;&#x629; &#x634;&#x627;&#x645;&#x644;&#x629; &#x628;&#x64A;&#x646; &#x631;&#x64A;&#x627;&#x643;&#x62A; React &#x648; Next.js</title><link>https://academy.hsoub.com/programming/javascript/react/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%B4%D8%A7%D9%85%D9%84%D8%A9-%D8%A8%D9%8A%D9%86-%D8%B1%D9%8A%D8%A7%D9%83%D8%AA-react-%D9%88-nextjs-r2326/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_05/--Next.js--React.png.6d7c9bc9071b201cb84f13e767f023fd.png" /></p>
<p>
	نكتشف في هذا المقال الاختلافات بين مكتبة رياكت React وإطار العمل Next.js وهو إطار عمل مشهور لجافا سكريبت تستخدمه شركات عديدة مثل تيك توك TikTok وهولو Hulu ونايكي Nike لتحسين تطبيقات الويب الخاصة بها. كما سنتعرّف على متى وكيف نستخدم Next.js، ونسرد بعض النصائح حول مفهوم العرض أو التصيير rendering والتوجيه والتصفح والتنقل في هذه التطبيقات.
</p>

<p>
	يعدّ Next.js إطار عمل سريع جدًا تعتمد عليه المواقع التي تتطلب في عملها حجم بيانات كبير مثل هولو Hulu ونيتفلكس Netflix. فإذا كنت بالفعل ملمًا بمكتبة ريآكت React، فيجب أن تتعرف بالتأكيد على هذه التقنية التي تزداد انتشارًا يومًا بعد يوم، فعلى الرغم من أن كلاً من React و Next.js يساعدان في تصميم واجهات مستخدم فعالة لمواقع الويب، إلا أنّ لديهما بعض الاختلافات الرئيسية، حيث يعد Next.js أكثر تطورًا وتفصيلًا من React، وهو مناسب تحديدًا للمواقع التي تهتم بتحسين محركات البحث SEO أو التصيير المسبق pre-rendering.
</p>

<h2 id="nextjsreact-1">
	Next.js أم React
</h2>

<p>
	ظهر إطار العمل رياكت <a href="https://wiki.hsoub.com/React" rel="external">React</a> أو ما يعرف كذلك باسم ReactJS أو React.js لأول مرة في عام 2013، وهو أكثر شهرة وانتشارًا من Next.js بكثير، ولكن الإطار <a href="https://wiki.hsoub.com/Next.js" rel="external">Next.js</a> الذي صدر عام 2016 بدأ بالانتشار وازدات شعبيته شيئًا فشيئًا وحصل على أكثر من 117 ألف نجمة على جيتهاب GitHub حتى تاريخ شهر يناير 2024 إلى جانب ملايين التنزيلات الأسبوعية له من مدير الحزم <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-%D9%81%D9%8A-nodejs-r1465/" rel="">npm</a>. دعونا نرى مقارنة بسيطة في الأداء بين Next.js و React التي توضح متى يجب أن نستخدم Next.js ومتى نستخدم React:
</p>

<ul>
	<li>
		<p>
			<strong>سرعة التطوير</strong>: يقدم Next.js ميزات حديثة ومبتكرة تسهل عملية التطوير لتصميم تطبيق React متقدم، ومع إدخال ميزة المجمّع compiler الخاص به في الإصدار Next.js 12، زادت سرعة التطوير فيه أيضًا بالمقارنة مع رياكت React، وبالتالي يقلّل Next.js من الوقت الذي ينتظره المبرمج لتحديث الشيفرة البرمجية، مما يقلّل من الإحباط والوقت الضائع خلال التطوير.
		</p>
	</li>
	<li>
		<p>
			<strong>جلب البيانات وأوقات التحميل</strong>: يمكن لإطار Next.js عبور <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-react-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-react-components-r834/" rel="">شجرة React</a> والاستعلام عن البيانات الموجودة في الخادم، مما يسمح بالتحميل المُسبق لبيانات الصفحة. وهذا يؤدي في كثير من الأحيان إلى تقليل أوقات تحميل التطبيق للصفحات المصممة باستخدام Next.js مقارنة بالصفحات المكتوبة بإطار React العادي فحسب.
		</p>
	</li>
	<li>
		<p>
			<strong>التصيير وتحسين محركات البحث</strong>: يتميز Next.js بميزة التصيير المُسبق، بينما يستخدم React التصيير المُعتمد على المستخدم وتوفر الصفحات المُصيَّرة والمعدة مسبقًا استراتيجيات تحسين محركات بحث فعالة الصعب تحقيقها في تطبيق React عادي.
		</p>
	</li>
	<li>
		<p>
			<strong>التوجيه</strong>: يوفر Next.js نظام ملفات مبني ومحدّد مسبقًا لعملية التوجيه routing. ويساهم هذا النظام في تقليل المرونة مقارنة بالخيارات المتنوعة لمكتبة React (مثل مكتبة <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D8%A9-react-router-r1166/" rel="">React Router</a>)، ولكنه يبسّط عملية إعداد الصفحة والتوجيه.
		</p>
	</li>
</ul>

<p>
	يخدم React مجموعة متنوعة من أنواع المشاريع بشكل جيد جدًا، بما في ذلك واجهات المستخدم، وأنظمة تطوير الواجهات الخلفية، وأدوات التنظيم الداخلية، وأنظمة عرض البيانات. Next.js هي أداة مثالية لتحسين تطبيقات React التي تستفيد من عملية التصيير المسبق، بما في ذلك المتاجر الإلكترونية، وتطبيقات وسائل التواصل الاجتماعي، وأنظمة حجز التذاكر، والمنصات التعليمية.
</p>

<p>
	دعونا نستكشف بعض حالات استخدام Next.js بمزيد من التفصيل، حتى تتمكن من الإجابة عن السؤال "هل يجب أن أستخدم Next.js؟ في تطبيقات الويب أم لا"
</p>

<h2 id="nextjs">
	التصيير في Next.js
</h2>

<p>
	التصيير rendering هو العملية التي تحول شيفرة React إلى HTML حيث يعرضه المتصفح كواجهة مستخدم ضمن الصفحة. يقدّم Next.js ثلاث طرق للتصيير
</p>

<ul>
	<li>
		التصيير المعتمد على المستخدم Client-side rendering أو CSR اختصارًا
	</li>
	<li>
		تصيير الخادم Server-side rendering أو SSR اختصارًا
	</li>
	<li>
		توليد الموقع الثابت static side generation أو SSG اختصارًا
	</li>
	<li>
		بالإضافة إلى إعادة التوليد الثابت التدريجي incremental static regeneration أو ISR اختصارًا.
	</li>
</ul>

<p>
	يجمع ISR بين تصيير الخادم مع آلية تخزين شبه ثابتة تخفف من الضغط على الخادم، وتوفر سرعات مشابهة لتلك التي يتم تحقيقها من خلال موقع ثابت. ويندرج تصيير الخادم وتوليد موقع ثابت تحت مظلة التصيير المسبق، حيث يتم إنشاء صفحات HTML قبل إرسالها إلى جهة المستخدم. ولعل الميزة الكبيرة التي يوفرها استخدام إطار Next.js هي أنه يضيف دعمًا قويًا لتصيير تطبيقات React.
</p>

<h3 id="">
	1. تصيير المستخدم
</h3>

<p>
	تصيير المستخدم هو التصيير الافتراضي لتطبيقات React الأساسية. تُولد هذه الطريقة صفحات HTML على جانب المستخدم. بمعنى آخر، تحدث عمليات التصيير في متصفح المستخدم، ويولد جافا سكريبت من جهاز المستخدم شيفرة HTML. تظهر واجهة المستخدم بعد اكتمال التصيير، عندما يكون الموقع تفاعلي أيضًا (أي، مُغذى hydrated).
</p>

<p>
	يكون استدعاء المستخدم ممكنًا لمكونات Next.js باستخدام <code>useEffect</code> أو <code>useSWR</code> في React.
</p>

<h3 id="-1">
	2. تصيير الخادم
</h3>

<p>
	يُتيح Next.js أيضًا إنشاء صفحة HTML على الخادم. في هذه الحالة، يتم إرسال شيفرة HTML المُولَّد إلى جانب المستخدم بحيث تظهر واجهة موقع الويب قبل التفعيل. ثم يكون موقع الويب القابل للعرض جاهزًا للاستخدام بعد اكتمال تهيئة جافا سكريبت على جانب المستخدم.
</p>

<p>
	تُضاف بعض دوال التكوين البسيطة إلى الصفحة في الصفحات التي نرغب في أن يؤدي Next.js تصيير الخادم فيها.
</p>

<h3 id="-2">
	3. توليد موقع ثابت
</h3>

<p>
	يقدّم Next.js أيضًا إمكانية توليد موقع ثابت، حيث يتم توليد جميع صفحات HTML الثابتة من JavaScript أثناء وقت البناء. ويتطلب توليد موقع ثابت من شيفرة React وقت بناء أكثر من تطبيق React ذي صفحة واحدة. ومع ذلك، الفائدة هنا هي وجود محتوى ثابت يمكن تخديمه وتخزينه بأقصى سرعة يسمح بها محتوى الموقع دون الأحمال الحسابية الزائدة لاستدعاء الخادم.
</p>

<p>
	يُمكننا تنفيذ SSG على صفحات Next.js التي نرغب في تصميمها بشكل ثابت باستخدام تعليمة ‎<code>‎getStaticProps(‎)‎</code>‎ وتعليمة <code>getStaticPaths()‎</code>، والأخير يحدد المسارات للصفحات الثابتة.
</p>

<h2 id="nextjs-1">
	تحسين محركات البحث في Next.js
</h2>

<p>
	تسمح السرعة والقدرة على استدعاء جميع صفحات موقع ويب في Next.js لمحركات البحث بتصفح وفهرسة الموقع بسرعة وسهولة، مما يعزز من عملية <a href="https://academy.hsoub.com/files/28-%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A5%D9%84%D9%89-%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D9%85%D8%AD%D8%B1%D9%83%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-seo/" rel="">تحسين محركات البحث</a>. وتحسين محركات البحث أمر ضروري للعديد من الشركات والمواقع الإلكترونية لأن المواقع التي تمّ تحسين محركات البحث فيها بشكل أفضل؛ تظهر في أعلى النتائج عند البحث. والمواقع ذات التصنيف الأعلى تكون أكثر احتمالًا للنقر عليها من قبل المستخدمين، حيث يصل معدل نقر المستخدمين إلى 27.6% للنتيجة الأولى، وهو معدل يزيد عشر مرات عن معدل نقر المستخدمين على النتيجة العاشرة والذي يبلغ 2.4%.
</p>

<p>
	في حين تواجه مواقع React التي تحتوي على كميات كبيرة من المحتوى - الذي يستخدم شيفرة جافا سكريبت للتصيير - تحديات في تحسين محركات البحث عند التعامل مع تصفح وفهرسة جوجل.
</p>

<p>
	<strong>ملاحظة</strong>: إن قدرة Next.js على التصيير من طرف الخادم بسهولة لا تُحسِّن فقط ترتيب الصفحة على محركات البحث فحسب، بل تُحسِّن أيضًا سرعة التحميل الفعلي للموقع من أجل تجربة مستخدم مثالية.
</p>

<h2 id="nextjs-2">
	البدء مع Next.js
</h2>

<p>
	لنلقي نظرة على أساسيات تهيئة Next.js والتوجيه والتصفح والتنقل حتى تتمكن من جني فوائد التصيير المسبق وتحسين محركات البحث. قبل البدء تأكد من تحميل أحدث إصدار من Node.js على جهازك. يمكنك التحقق من إصدار Node.js على جهازك باستخدام الأمر
</p>

<pre class="ipsCode" id="ips_uid_9177_12"> node --version</pre>

<p>
	وهناك طريقتان لتهيئة مشروع Next.js:
</p>

<ol>
	<li>
		تهيئة تلقائية، مع ضبط محدد مسبقًا.
	</li>
	<li>
		تهيئة يدوية مع الضبط.
	</li>
</ol>

<p>
	سنتبع التهيئة التلقائية للبدء في مشروعنا بشكل أسهل. تدير أداة سطر الأوامر CLI أو <code>create-next-app</code> التهيئة التلقائية وتجعل من الممكن تصميم التطبيقات بسرعة. دعونا ننشئ مشروعنا مع دعم Next.js المدمج للغة <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-javascript-%D9%88-typescript-r1598/" rel="">TypeScript</a>؛ وهي لغة مشتقة من <a href="https://academy.hsoub.com/javascript/" rel="">جافا سكريبت</a> تضمن نوع متغيرات صحيح:
</p>

<pre class="ipsCode">npx create-next-app@latest --typescript
</pre>

<p>
	سيطلب <code>create-next-app</code> اسم تطبيقك. يجب أن يتكون اسم مشروعك من حروف صغيرة وأن يستخدم الشرطات بدلاً من المسافات. على سبيل المثال، اخترنا الاسم <code>next-js-tutorial</code>. بمجرد اكتمال التهيئة، سترى رسالة نجاح على شاشتك.
</p>

<p>
	يفرض Next.js نظامًا صارمًا لهيكلة الملفات، ففي مشروعنا الجديد سنجد الهيكلية التالية:
</p>

<ul>
	<li>
		تُنظم الصفحات <code>pages</code> وتنسيق الموقع <code>styles</code> في مجلداتها الخاصة.
	</li>
	<li>
		تُخزَّن واجهات برمجة التطبيقات أو <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> في مجلد <code>pages/api</code>.
	</li>
	<li>
		تحفظ البيانات المتاحة للعامة في المجلد <code>public</code>.
	</li>
</ul>

<h2 id="nextjs-3">
	التوجيه والصفحات في Next.js
</h2>

<p>
	يستخدم Next.js هيكلة الملفات داخل مسار الصفحات <code>pages</code> لتحديد مسارات التطبيق. حيث نعرّف جميع المسارات في مجلد <code>pages</code>، ويُعدّ ملف<br>
	<code>pages/index.tsx</code> هو نقطة الدخول للتطبيق حيث نحدد الخطوط المخصصة، وأكواد تتبّع التطبيق، وغيرها من العناصر التي تتطلب وصولًا عامًا.
</p>

<p>
	هناك طريقتان لإضافة مسارات جديدة:
</p>

<ol>
	<li>
		إضافة ملف ينتهي بـ <code>.tsx</code> مباشرة في المجلد <code>pages</code>.
	</li>
	<li>
		إضافة ملف <code>index.tsx</code> ضمن مجلد فرعي جديد من المجلد <code>pages</code> (الملفات <code>index</code> توجّه تلقائيًا إلى بداية المجلد).
	</li>
</ol>

<p>
	دعونا نختبر عدة أمثلة محددة لـ Next.js لعملية التوجيه. سننفذ مسار صفحة بسيط، ثم سنتطرق إلى مفاهيم التوجيه المتداخل والديناميكي.
</p>

<h3 id="-3">
	مسارات الصفحة
</h3>

<p>
	يمكننا إضافة مسار صفحة أساسي، مثل <code>about-us</code>، باستخدام ملف <code>about-us.tsx</code>:
</p>

<pre class="ipsCode">|— pages
|    |— _app.tsx
|    |— about-us.tsx
|    |— api
|    |— index.tsx
</pre>

<p>
	أو يمكننا استخدام ملف <code>index.tsx</code> ضمن مجلد <code>pages/about-us</code>:
</p>

<pre class="ipsCode">|— pages
|    |— _app.tsx
|    |— about-us
|    |    |— index.tsx
|    |— api
|    |— index.tsx
</pre>

<p>
	استمر وأضف مسار صفحة <code>about-us.tsx</code> إلى مشروعك.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9177_15" style=""><span class="kwd">import</span><span class="pln"> styles from </span><span class="str">'../styles/Home.module.css'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">AboutUs</span><span class="pun">:</span><span class="pln"> </span><span class="typ">NextPage</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> 
    </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">container</span><span class="pun">}&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">main className</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">main</span><span class="pun">}&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">h1 className</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">title</span><span class="pun">}&gt;</span><span class="pln">
          </span><span class="typ">About</span><span class="pln"> </span><span class="typ">Us</span><span class="pln"> </span><span class="typ">Example</span><span class="pln"> </span><span class="typ">Page</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">main</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">AboutUs</span></pre>

<p>
	سنرى توجيه الصفحة فعليًا عند استخدامها بالتزامن مع التنقل في Next.js. في الوقت الحالي، سنعيد NextPage ككلمة مؤقتة مع سلسلة عنوان حتى يعمل التنقل بشكل صحيح.
</p>

<h2 id="-4">
	المسارات المتداخلة
</h2>

<p>
	تسمح المسارات المتداخلة nested routes بإعادة استخدام عدة تخطيطات اختيارية على صفحة (على سبيل المثال، عندما ينقر المستخدم على عنوان URL، قد ترغب في تحديث محتوى الجسم والاحتفاظ بتخطيطات الرأس والتذييل).
</p>

<pre class="ipsCode">|— pages
|    |— _app.tsx
|    |— api
|    |— index.tsx
|    |— parent
|    |    |— child.tsx
</pre>

<p>
	نحدد المسار المتداخل<code>/parent/child</code> باستخدام مجلد <code>parent</code> ومجلد أو ملف <code>child</code> متداخل (مثالنا يوضح ملفًا).
</p>

<h3 id="-5">
	المسارات الديناميكية
</h3>

<p>
	تسمح <a href="https://wiki.hsoub.com/Next.js/Routing#.D8.A7.D9.84.D9.88.D8.AC.D9.87.D8.A7.D8.AA_.D8.A7.D9.84.D8.AF.D9.8A.D9.86.D8.A7.D9.85.D9.8A.D9.83.D9.8A.D8.A9_Dynamic_Routes" rel="external">المسارات الديناميكية dynamic routes</a> للتخطيطات بالاستجابة للتغييرات في الوقت الحقيقي عندما لا يفي استخدام المسارات المعرفة مسبقًا<br>
	pre-defined paths بالغرض. على سبيل المثال، إذا كنا نرغب في إنشاء مسار <code>/product/[productId]‎</code> (أي عند النقر على منتج، يتوسع مكونه). يُمكننا بسهولة إضافة مسار ديناميكي بافتراض أن <code>productId</code> هو متغير قابل للوصول في تعريفنا لمجلد <code>product</code>، وذلك عبر إنشاء مجلد <code>product</code> وتضمين صفحة <code>productId</code> بين قوسين:
</p>

<pre class="ipsCode">|— pages
|    |— _app.tsx
|    |— api
|    |— index.tsx
|    |— product
|    |    |— [productId].tsx
</pre>

<p>
	بهذه الطريقة، سيكون لديك مسار مثل <code>product/testId</code> ستُضبط فيه قوائم الإعدادات (أي <code>productId</code> يُضبط إلى <code>testId</code>).
</p>

<p>
	أخيرًا، من الممكن أيضًا دمج تقنيات التوجيه. على سبيل المثال، يُمكننا إنشاء مسار ديناميكي مُتداخِل <code>pages/post/[postId]/[comment].tsx</code>.
</p>

<h2 id="nextjs-4">
	التنقل في Next.js
</h2>

<p>
	يستخدم Next.js مكونات <code>Link</code> خاصة به بدلاً من وسم <code>&lt;a&gt;</code> في HTML عند التنقل بين صفحات المستخدم للسماح لـNext.js بتحسين التنقل واسترجاع البيانات مسبقًا. يعمل <code>Link</code> بشكل مماثل لوسم <code>&lt;a&gt;</code> ويستخدم <code>href</code> كالمسار الذي سيتم فتحه. يجب عليك استخدام خاصية <code>passHref</code> لإجبار <code>Link</code> على تمرير قيمة مساره إلى المكونات الفرعية (على سبيل المثال، عند استخدام مكونات مخصصة المظهر).
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9177_17" style=""><span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">className</span><span class="pun">=</span><span class="atv">{styles.description}</span><span class="tag">&gt;</span><span class="pln">
  Here's our example </span><span class="tag">&lt;Link</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"/about-us"</span><span class="tag">&gt;</span><span class="pln">About Us</span><span class="tag">&lt;/Link&gt;</span><span class="pln"> page
</span><span class="tag">&lt;/p&gt;</span><span class="pln"> </span></pre>

<p>
	الفائدة الرئيسية من استخدام <code>Link</code> بدلاً من وسم <code>&lt;a&gt;</code> هي أنه يحمّل البيانات مسبقًا في الخلفية عندما يضغط المستخدم فوق الرابط أو بالقرب منه. يجعل هذا المحتوى متاحًا بشكل أكبر للمستخدم لمعالجته، مما يؤدي إلى تحسين أداء التطبيق. وما زال بإمكانك استخدام وسم <code>&lt;a&gt;</code> في Next.js عند الربط بصفحات خارجية خارج التطبيق.
</p>

<p>
	على سبيل المثال للربط بصفحة <code>About Us</code> من مشروع Next.js، افتح ملف التطبيق الرئيسي <code>pages/index.tsx</code>. سنستورد أولًا مكون الرابط <code>Link</code> من <code>next/link</code>، ثم نضيف فقرة مرتبطة تحت مكون<code>&lt;h1&gt;</code>:
</p>

<p>
	الآن يمكننا تشغيل التطبيق باستخدام الأمر <code>npm run dev</code>، اذهب إلى <a href="http://xn--localhost%3A3000-8nr" rel="external nofollow">http://localhost:3000،</a> ثم شاهد النص المضاف وصفحة <code>About Us</code> تعمل على <a href="http://localhost:3000/about-us" rel="external nofollow">http://localhost:3000/about-us</a> (المسار المُعاد بعد النقر على About Us).
</p>

<h2 id="nextjs-5">
	الخلاصة
</h2>

<p>
	هناك العديد من العوامل التي يجب مراعاتها قبل اختيار <a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">إطار عمل</a> لموقع الويب القادم. على الرغم من أن Next.js أكثر تفصيلًا وأقل مرونة من React، إلا أن الإطار يوفر وظائف مبتكرة جاهزة للاستخدام لمشاريع متقدمة تستهدف تحسين محركات البحث أو إمكانيات العرض المسبق. باستخدام أساسيات Next.js ضمن أدواتك، يمكنك تعزيز موقعك والحصول على ميزات في تطبيقات React الأساسية.
</p>

<h2 id="-6">
	أسئلة شائعة
</h2>

<p>
	نختم مقالنا بمجموعة من الأسئلة الشائعة المتعلقة بإطار عمل Next.js ورياكت React
</p>

<h3>
	1. لماذا أستخدم Next.js؟
</h3>

<p>
	يجعل Next.js من عملية بناء بعض واجهات مواقع الويب عملية أسهل كما يضيف مزايا على React، ويقدّم مجتمعًا قويًا وتوثيقًا جيدًا، كما أنه يكسب شعبية كبيرة بين مطوري الواجهات الأمامية.
</p>

<h3>
	2. هل React أفضل من Next.js؟
</h3>

<p>
	لا، React ليس أفضل من Next.js، يجب على المطورين أن يختاروا إطار العمل بناءً على ما يصلح لمشروعهم وبناءً على أولوياتهم (قد يستفيد موقع يركّز على تحسين محركات البحث من Next.js على سبيل المثال، وذلك بالنظر إلى أنه يقلّل من وقت تحميل التطبيق).
</p>

<h3>
	3. لم يُنظر إلى Next.js بكونه أفضل من React؟
</h3>

<p>
	يقدم Next.js تحسينات على سرعة التطوير ويقلل من وقت تحميل التطبيق، كما يحتوي على التصيير المسبق.
</p>

<h3>
	4. ما الفرق بين Next.js و React؟
</h3>

<p>
	إن Next.js إطار عمل مبني على React مع بعض المزايا المُضافة بشكل افتراضي، React هو مكتبة جافا سكريبت شائعة تُستخدم لبناء واجهات المستخدم التفاعلية، بالأخص في تطبيقات الويب.
</p>

<h3>
	5. ما هي المشاكل التي يحلها Next.js؟
</h3>

<p>
	يقدّم Next.js التصيير المسبق ويمكن أن يحسن من نتائج الموقع على محركات البحث مع تقليل وقت التحميل.
</p>

<h3>
	6. لم قد أختار React.js؟
</h3>

<p>
	يعدّ React إطارًا أقلّ تحكمًا وأكثر شعبية من Next.js ويقدم للمطورين زيادة في المرونة وموارد عديدة.
</p>

<h3>
	7. هل React وReact.js يشيران للشيء ذاته؟
</h3>

<p>
	نعم، كلا المصطلحين يشيران إلى مكتبة جافا سكريبت ذاتها وهي مكتبة JavaScript تُستخدَم لبناء واجهات المستخدم.
</p>

<h3>
	8. هل يعد Next.js إطار عمل؟
</h3>

<p>
	نعم، Next.js إطار عمل مفتوح المصدر مبني على React.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://www.toptal.com/next-js/next-js-vs-react" rel="external nofollow">Next.js vs React: A Comparative Tutorial</a> لصاحبه Ayyaz Ali
</p>

<h1>
	اقرأ أيضًا
</h1>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%83%D9%84-%D9%85%D8%A7-%D8%AA%D9%88%D8%AF-%D9%85%D8%B9%D8%B1%D9%81%D8%AA%D9%87-%D8%B9%D9%86-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B1%D9%8A%D8%A2%D9%83%D8%AA-%D9%86%D9%8A%D8%AA%D9%81-react-native-r2199/" rel="">كل ما تود معرفته عن إطار ريآكت نيتف React Native</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A7%D9%84%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-react-r774/" rel="">المصطلحات المستخدمة في React</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%B5%D8%A7%D8%AF%D8%B1-%D8%AA%D8%B9%D9%84%D9%85-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-react-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1575/" rel="">مصادر تعلم مكتبة React لبناء تطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">مقدمة إلى Node.js</a>
	</li>
</ul>

<p>
	 
</p>
]]></description><guid isPermaLink="false">2326</guid><pubDate>Sat, 11 May 2024 12:01:06 +0000</pubDate></item><item><title>&#x643;&#x644; &#x645;&#x627; &#x62A;&#x648;&#x62F; &#x645;&#x639;&#x631;&#x641;&#x62A;&#x647; &#x639;&#x646; &#x625;&#x637;&#x627;&#x631; &#x631;&#x64A;&#x622;&#x643;&#x62A; &#x646;&#x64A;&#x62A;&#x641; React Native</title><link>https://academy.hsoub.com/programming/javascript/react/%D9%83%D9%84-%D9%85%D8%A7-%D8%AA%D9%88%D8%AF-%D9%85%D8%B9%D8%B1%D9%81%D8%AA%D9%87-%D8%B9%D9%86-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B1%D9%8A%D8%A2%D9%83%D8%AA-%D9%86%D9%8A%D8%AA%D9%81-react-native-r2199/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2023_12/--.png.da9fac8e2a82a437f11618054511dd0b.png" /></p>
<p>
	ستتعرف في مقال اليوم على رياكت نيتف React Native أحد أشهر أطر عمل لغة البرمجة جافا سكريبت JavaScript والذي يمكنك من خلاله إنشاء تطبيقات جوال متعددة المنصات تعمل على نظامي التشغيل أندرويد Android و أي أو إس iOS بسهولة ومرونة.
</p>

<h2 id="reactnative">
	ما هو React Native
</h2>

<p>
	ريآكت نيتف React Native هو إطار عمل <a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">framework</a> مفتوح المصدر للغة البرمجة جافا سكريبت <a href="https://wiki.hsoub.com/JavaScript" rel="external" target="_blank">JavaScript</a> طورته شركة ميتا Meta أو فيسبوك سابقًا عام 2015 لتمكن المطورين من تطوير الواجهات الأمامية لتطبيقات الجوال متعددة المنصات cross platform، أي أن التطبيقات المطورة باستخدام هذا الإطار يمكنها العمل على أنظمة تشغيل الجوال الأساسية مثل أندرويد Android وآي أو إس iOS بذات الوقت.
</p>

<p>
	تتميز التطبيقات المطورة باستخدام ريأكت نيتف React Native بكونها تشبه التطبيقات الأصيلة Native apps وأسلوب تطوير تطبيقات أصيلة هو أحد <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D9%84%D9%8A%D8%A8-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AC%D9%88%D8%A7%D9%84-r2065/" rel="">أساليب تطوير تطبيقات الجوال</a> المصممة للعمل على نظام تشغيل محدد فقط، فكي تتمكن من تطوير تطبيق جوال أصيل تحتاج لأن تبرمج التطبيق بلغة البرمجة الأم الخاصة بكل نظام تشغيل على حدا مثل لغة Objective-C أو Swift لنظام أندرويد ولغة جافا Java أو كوتلن <a href="https://academy.hsoub.com/programming/kotlin/kotlin-%D9%87%D9%88-%D8%AC%D8%A7%D9%81%D8%A7-%D8%A7%D9%84%D8%AC%D8%AF%D9%8A%D8%AF-r641/" rel="">Kotlin</a> لنظام iOS.
</p>

<p>
	أما في ريأكت نيتف فالتطبيقات التي تطورها تعتبر شبه أصيلة بمعنى أنك لست بحاجة للالتزام بلغة نظام التشغيل لتطوير تطبيق مخصص لهذا النظام بل يمكنك كتابة كود التطبيق مرة واحدة بلغة جافا سكريبت <a href="https://wiki.hsoub.com/JavaScript" rel="external" target="_blank">JavaScript</a> وتصديره للعمل على مختلف المنصات وهذا يسهل ويسرع تطوير التطبيقات بشكل كبير.
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="499" id="ips_uid_3582_5" src="https://academy.hsoub.com/applications/core/interface/index.html" title="ما هي تطبيقات الجوال متعددة المنصات" width="887" data-embed-src="https://www.youtube.com/embed/nMnBfl-fQJo"></iframe>
</p>

<h2 id="reactnative-1">
	استخدامات React Native
</h2>

<p>
	يتيح لك إطار React Native إنشاء تطبيقات الهاتف المحمول وتطوير واجهات مستخدم تفاعلية وغنية بالمميزات لا يمكن تمييزها عن التطبيقات الأصيلة المبنية باللغات الأساسية للمنصات بسرعة وسهولة باستخدام لغة جافا سكريبت <a href="https://academy.hsoub.com/javascript/" rel="">JavaScript</a> ومكتبة ريآكت <a href="https://wiki.hsoub.com/React" rel="external" target="_blank">React</a>.
</p>

<p>
	فهو يحول كافة المكونات إلى تعليمات برمجية أصيلة ويجعل مظهرها مماثلًا لعناصر تطبيقات أنظمة أندوريد Android الخاصة بهواتف سامسونج وغيرها، أو تطبيقات أي أو إس iOS الخاصة بأجهزة آيفون، لكن لا يستخدم ريآكت نيتف في إنشاء تطبيقات ويب هجينة أو تطبيقات مختلطة Hybrid apps تعمل ضمن عارض ويب WebView.
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="499" id="ips_uid_3582_6" src="https://academy.hsoub.com/applications/core/interface/index.html" title="أساسيات React Native" width="887" data-embed-src="https://www.youtube.com/embed/wO78tgIf5Es"></iframe>
</p>

<h2 id="reactnative-2">
	إيجابيات إطار React Native
</h2>

<p>
	إذا كنت تفكر في <a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82/" rel="">برمجة تطبيق</a> جوال فإن استخدام إطار React Native خيار مناسب ويوفر لك العديد من المميزات التي قد لا تتوفر في أطر العمل الأخرى، ومن أبرز هذه المميزات:
</p>

<ul>
	<li>
		يعتمد إطار عمل ريآكت نيتف React Native على لغة البرمجة جافا سكريبت <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%B5%D9%81%D8%B1-%D8%AD%D8%AA%D9%89-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D8%B1%D8%A7%D9%81-r2046/" rel="">JavaScript</a> التي تعد واحدة من أشهر وأقوى لغات البرمجة <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D9%87%D9%84-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">وأسهلها في التعلم</a>.
	</li>
	<li>
		يمكن من خلال ريآكت نيتف <a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82/" rel="">برمجة تطبيقات</a> متوافقة مع أنظمة الجوال الأساسية أندرويد Android و آي أو إس iOS بسهولة وسرعة وبأقل وقت وتكلفة باستخدام نفس الكود البرمجي.
	</li>
	<li>
		يمكن من خلاله تطوير تطبيقات فعالة قادرة على الوصول لموارد الهاتف مثل الكاميرا و الموقع ولها نفس مظهر التطبيقات الأخرى على الهاتف مما يحسن تجربة المستخدم.
	</li>
	<li>
		يملك إطار ريآكت نيتف شعبية هائلة بين أوساط المطورين فقد حصل على أكثر من 112 نجمة على <a href="https://github.com/facebook/react-native" rel="external nofollow" target="_blank">مستودع Github</a>، كما يملك مجتمع دعم كبير ونشط يوفر ميزات وأدوات جديدة باستمرار كما يوفر مصادر تعلم منوعة تساعدك في التعلم وحل أي مشكلة تواجهك.
	</li>
	<li>
		يستخدم ريآكت نيتف في تطوير تطبيقات <a href="https://stackshare.io/react-native" rel="external nofollow" target="_blank">العديد من الشركات الكبرى</a> ومن أبرزها تطبيق التواصل الاجتماعي فيسبوك، كما تستخدمه شركة إنستغرام Instagram وشوبيفاي <a href="https://academy.hsoub.com/apps/web/%D8%A3%D9%8A%D9%87%D9%85%D8%A7-%D8%A3%D9%81%D8%B6%D9%84%D8%8C-%D8%B4%D9%88%D8%A8%D9%8A%D9%81%D8%A7%D9%8A-%D8%A3%D9%85-%D9%88%D9%88%D9%83%D9%88%D9%85%D8%B1%D8%B3%D8%9F-r346/" rel="">Shopify</a> وديسكورد Discord وغيرها ما يعزز من شهرته وموثوقيته.
	</li>
	<li>
		يوفر الكثير من المكتبات والحزم والمكونات مفتوحة المصدر التي يمكن إعادة استخدامها بسهولة لتطوير تطبيقات جوال بوظائف ومميزات احترافية دون الحاجة لكتابة كافة الأكواد من الصفر ومن أبرزها مكتبة React Navigation التي تبسط عمليات التنقل ضمن شاشات التطبيق المختلفة.
	</li>
	<li>
		يوفر ميزة إعادة التحميل السريع (hot reloading) التي تمكن المطور من رؤية التغييرات التي يجريها على التطبيق مباشرة دون الحاجة لإيقافه وإعادة تشغيله، أي بمجرد إنشاء تطبيقك يمكنك عكس أي تعديل في التعليمات البرمجية على وظائف التطبيق دون الحاجة إلى إعادة بنائه وهذه الميزة تسرع عمل <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A8%D8%B1%D9%85%D8%AC-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA/" rel="">مبرمج التطبيقات</a> وتزيد إنتاجيته.
	</li>
	<li>
		يوفر الكثير من الأدوات المساعدة التي تسهل عمل المطورين مثل منصة <a href="https://expo.dev/client" rel="external nofollow" target="_blank">Expo</a> التي توفر مجموعة من الأدوات والخدمات التي تسرع بشكل كبير إعداد بيئة العمل وبناء تطبيقات React Native وتعديلها وتصحيح أخطائها ونشرها.
	</li>
</ul>

<h2 id="reactnative-3">
	سلبيات إطار React Native
</h2>

<p>
	لا يخلو إطار ريآكت نيتف React Native من بعض العيوب شأنه شأن أي تقنية أخرى، ومن بينها نذكر:
</p>

<ul>
	<li>
		لا توفر المكونات القياسية في إطار ريآكت نيتف React Native كافة المميزات التي يحتاجها المطور لذا قد يضطر لاستيراد مكتبات خارجية لتضمينها، واستخدام الكثير من المكتبات الخارجية قد يؤثر على أداء التطبيق كما أنها قد تؤثر على أمانه إذا لم تحدّث بصورة مستمرة.
	</li>
	<li>
		قد لا تتوفر في بعض المكتبات الخارجية ومكونات الطرف الثالث التي يحتاج المطور لاستخدامها توثيقات مساعدة ووافية حول طريقة استخدامها والتعامل معها.
	</li>
	<li>
		قد تكون التطبيقات التي تتضمن الكثير من عمليات المعالجة بطيئة نسييًا، لكون لغة جافا سكريبت JavaScript غير مناسبة بشكل عام للمعالجة الثقيلة، أما بالنسبة للتطبيقات التي تحتاج فقط للتفاعل مع واجهات برمجة التطبيقات <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel=""><abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> أو التعامل مع قواعد البيانات فهو مناسب تمامًا.
	</li>
</ul>

<h2 id="">
	الفرق بين رياكت نيتف وفلاتر
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="139981" href="https://academy.hsoub.com/uploads/monthly_2023_12/----.png.28d5fab3092055e3a2fb7faa6bd0e4d1.png" rel=""><img alt="الفرق بين رياكت نيتف وفلاتر" class="ipsImage ipsImage_thumbnailed" data-fileid="139981" data-ratio="62.60" data-unique="kinatfo0i" style="width: 500px; height: auto;" width="500" src="https://academy.hsoub.com/uploads/monthly_2023_12/----.png.28d5fab3092055e3a2fb7faa6bd0e4d1.png"> </a>
</p>

<p>
	يتنافس إطار ريآكت نيتف React Native مع إطار فلاتر Flutter مفتوح المصدر الذي طورته جوجل عام 2017 هو لا يقل شهرة عن ريآكت نيتف حيث يملك أكثر من 157.6 ألف نجمة على موقع GitHub ويتميز بسرعته واستقراره.
</p>

<p>
	يملك كلا الإطارين الكثير من المميزات المتشابهة وكلاهما خيار مناسب لتطوير تطبيقات الجوال متوافقة مع مختلف الأنظمة الأساسية باستخدام نفس التعليمات البرمجية، لكن الاختلاف الأساسي بينهما هو أن ريآكت نيتف يستخدم لغة جافا سكربيت المشهورة، أما فلاتر فيستخدم لغة دارت Dart وهي لغة برمجة قوية وعالية الأداء طورتها جوجل لكنها لا تستخدم على نطاق واسع بين أوساط المبرمجين.
</p>

<p>
	كما يعد تثبيت إطار ريآكت نيتف أسهل من تثبيت إطار فلاتر حيث أن كل ما تحتاجه لتثبيته هو كتابة أمر واحد على الطرفية أما تثبيت فلاتر فيتطلب منك عدة خطوات وإعدادات على نظام تشغيلك كي يعمل بالشكل الصحيح.
</p>

<p>
	وبالطبع يعتمد اختيار إطار العمل على متطلبات تطبيقك وخبرتك فإذا كنت على دراية بلغة برمجة جافا سكريبت فسيكون ريآكت نيتف الخيار الأنسب وإذا كنت تهتم بسرعة وأداء التطبيق فمن الأنسب اختيار فلاتر.
</p>

<h2 id="-1">
	الفرق بين رياكت ورياكت نيتف
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="139979" href="https://academy.hsoub.com/uploads/monthly_2023_12/----.png.38bb8591e2f01dd197215de9648aae13.png" rel=""><img alt="الفرق بين رياكت و رياكت نيتف" class="ipsImage ipsImage_thumbnailed" data-fileid="139979" data-ratio="62.60" data-unique="flaxto7v6" style="width: 500px; height: auto;" width="500" src="https://academy.hsoub.com/uploads/monthly_2023_12/----.png.38bb8591e2f01dd197215de9648aae13.png"> </a>
</p>

<p>
	تعد كل من ريآكت React و ريآكت نيتف React Native تقنيتين شائعتين في تطوير التطبيقات وكلاهما مطور من قبل فيسبوك وكثيرًا ما يخلط المبتدئون بينهما بسبب تشابه أسمائهما لكن هناك أيضًا فروقات بينهما.
</p>

<p>
	حيث أن ريآكت <a href="https://wiki.hsoub.com/React" rel="external" target="_blank">React</a> أو ما يعرف كذلك باسم ReactJS أو React.js هي <strong>مكتبة جافا سكريبت</strong> مفتوحة المصدر مشهورة <a href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1071/" rel="">لتطوير تطبيقات الويب</a> ذات الصفحة الواحدة Single Page Apps أو اختصارًا SPA وهي تطبيقات تعتمد على تقنيات الويب وتسمح للمطورين بإنشاء واجهات مستخدم UI تفاعلية جميلة وأنيقة باستخدام مكونات قابلة لإعادة الاستخدام وتسهل وتسرع عمل المطورين وتستخدم هذه المكتبة لغة خاصة توسع لغة جافا سكريبت وتشبه <a href="https://wiki.hsoub.com/HTML" rel="external" target="_blank">HTML</a> تسمى <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B5%D9%8A%D8%BA%D8%A9-javascript-syntax-extension-jsx-r777/" rel="">JSX</a>.
</p>

<p>
	أما رياكت نيتف React Native فهو كما ذكرنا سابقًا <strong>إطار عمل</strong> <strong>جافا سكريبت</strong> يستخدم من أجل <a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AC%D9%88%D8%A7%D9%84-r1801/" rel="">تطوير تطبيقات الجوال</a> شبه أصيلة متعددة المنصات، وإطار العمل أشمل وأوسع من المكتبة فهو يعتمد على مبادئ مكتبة ريآكت القائمة على إعادة الاستخدام ويرث جميع وظائف ومميزات رياكت لتحسين واجهة المستخدم للتطبيقات كما يضيف العديد من المميزات الأخرى فإذا كنت على دراية بمكتبة React وطريقة كتابة تعليماتها فهذا سوف يساعدك على فهم React Native واستخدامه بسهولة وسلاسة.
</p>

<p>
	لذا يمكنك أن تستخدم مكتبة React إذا كنت تريد بناء تطبيقات ويب سريعة، بينما من الأفضل أن تعتمد على إطار React Native لبناء تطبيقات جوال متعددة المنصات.
</p>

<h2 id="reactnative-4">
	خطوات تعلم إطار العمل رياكت نيتف React Native
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="139977" href="https://academy.hsoub.com/uploads/monthly_2023_12/--.png.05481d6496e15873225a7398aede1fae.png" rel=""><img alt="تعلم رياكت نيتف" class="ipsImage ipsImage_thumbnailed" data-fileid="139977" data-ratio="62.60" data-unique="4gfy3elmo" style="width: 500px; height: auto;" width="500" src="https://academy.hsoub.com/uploads/monthly_2023_12/--.thumb.png.2b333392745b113f09fb1757ee4b193e.png"> </a>
</p>

<p>
	يحتاج تعلم إطار React Native لتأسيس في بعض التقنيات قبل البدء كي تتمكن من تعلمه بسهولة وسلاسة، وإليك أهم خطوات تعلم إطار React Native
</p>

<ul>
	<li>
		تعرف على <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8/" rel="">تقنيات تطوير تطبيقات الويب</a> الأساسية مثل HTML و CSS ومفهوم نموذج كائن المستند DOM فبالرغم من أنك لن تكتب أكوادها مباشرة في تطبيقات React Native إلا أنها تشكل أساسًا لفهم تقنيات أخرى ستحتاجها.
	</li>
	<li>
		ركز على تعلم تخطيط <a href="https://wiki.hsoub.com/CSS#.D8.AA.D8.AE.D8.B7.D9.8A.D8.B7_Flex_Box" rel="external" target="_blank">flexbox</a> في CSS فإطار React Native يستخدمه بشكل أساسي لتخطيط عناصر الواجهات.
	</li>
	<li>
		تعلم أساسيات لغة جافا سكريبت JavaScript وتعرف من خلالها على <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">أساسيات البرمجة</a> ومبادئها ومصطلحاتها الأساسية مثل المتغيرات والحلقات التكرارية والشروط والوظائف البرمجية وهياكل البيانات.
	</li>
	<li>
		تعلم كيفية استخدام واجهة برمجة التطبيقات <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel=""><abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> وإجراء الطلبات منها والحصول على البيانات وعرضها في تطبيقاتك.
	</li>
	<li>
		يمكنك كخطوة اختيارية تعلم مكتبة React وصيغة JSX التي تستخدمها لإنشاء مكونات واجهة المستخدم قبل الانتقال إلى تعلم إطار العمل React Native فهو كما ذكرنا سابقًا مبني على أساسها وتعلمها سيكون خطوة مفيدة تسهل الخطوات اللاحقة.
	</li>
	<li>
		تعلم كيفية <a href="https://github.com/vhpoet/react-native-styling-cheat-sheet" rel="external nofollow" target="_blank">كتابة التنسيقات</a> لمكونات تطبيقاتك لإنشاء واجهات مستخدم أنيقة واحترافية لتطبيقات الهواتف المحمولة.
	</li>
	<li>
		ابدأ بتعلم إطار React Native وإعداد بيئة التطوير وتعلم طريقة <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">استخدام سطر الأوامر في تطوير الويب</a> من طرف العميل وأهما أدوات سطر الأوامر مثل npm وتعرف على طريقة استخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%85%D8%B7%D9%88%D8%B1%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D9%85%D8%AF%D9%85%D8%AC%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1439/" rel="">أدوات مطور الويب</a> المدمجة في المتصفحات لتصحيح الأخطاء.
	</li>
	<li>
		تعلم <a href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-react-native-r1265/" rel="">أساسيات React Native</a> وأهم <a href="https://wiki.hsoub.com/ReactNative/components_and_apis" rel="external" target="_blank">المكونات</a> التي يوفرها لتطوير التطبيقات لنظامي iOS وAndroid.
	</li>
	<li>
		ابدأ بتطوير تطبيقات ريآكت نيتف بسيطة مثل تطبيق آلة حاسبة تطبيق أو تطبيق تدوين ملاحظات أو تطبيق توليد كلمات المرور فالممارسة العملية ضرورية لتعزيز كل ما تعلمته من مفاهيم نظرية حول ريأكت نتيف.
	</li>
	<li>
		انتقل لتطوير تطبيقات ريآكت نيتف متكاملة لبناء معرض أعمال خاص بك وتدرب من خلالها على كافة خطوات تطوير التطبيقات الأصيلة بدءًا من تحديد فكرة التطبيق حتى مرحلة نشره وإصداره على كل منصة.
	</li>
</ul>

<p>
	عند إتمامك لكل الخطوات السابقة ستكون جاهزًا الآن للبحث عن فرص عمل مناسبة في شركة تطوير فعلية، العمل كمستقل و عرض خدماتك في تطوير التطبيقات للعملاء الفعلين على منصة خاصة بك أو على مواقع العمل الحر مثل <a href="https://mostaql.com/" rel="external" target="_blank">مستقل</a> أو <a href="https://khamsat.com/" rel="external" target="_blank">خمسات</a>.
</p>

<h2 id="reactnative-5">
	مصادر تعلم رياكت نيتف React Native
</h2>

<p>
	تتوفر الكثير من المصادر لمساعدتك على تعلم إطار React Native ومن بين مصادر التعلم المفيدة التي ستفيدك جدًا الدروس والمقالات الدورية التي توفرها أكاديمية حسوب حول <a href="https://academy.hsoub.com/programming/javascript/" rel="">جافا سكريبت</a> وأهم مكتباتها وأطر عملها وعلى رأسها <a href="https://academy.hsoub.com/programming/javascript/react/" rel="">React</a>، كما ستجد في <a href="https://wiki.hsoub.com/%D8%A7%D9%84%D8%B5%D9%81%D8%AD%D8%A9_%D8%A7%D9%84%D8%B1%D8%A6%D9%8A%D8%B3%D9%8A%D8%A9" rel="external" target="_blank">موسوعة حسوب</a> توثيقًا وافيًا باللغة العربية لمختلف لغات البرمجة وتقنيات الويب والجوال ومن ضمنها توثيق شامل لإطار العمل <a href="https://wiki.hsoub.com/ReactNative" rel="external" target="_blank">React Native</a> ومكتبة <a href="https://wiki.hsoub.com/React" rel="external" target="_blank">React</a>.
</p>

<p>
	وإذا كنت تفضل التعلم من الكتب فقد وفرت أكاديمية حسوب العديد من <a href="https://academy.hsoub.com/files/" rel="">الكتب</a> المميزة والمجانية باللغة العربية التي تساعدك على تعلم الكثير حول لغة البرمجة جافا سكريبت والعديد من اللغات والتقنيات الأخرى.
</p>
<iframe allowfullscreen="" class="ipsEmbed_finishedLoading" data-controller="core.front.core.autosizeiframe" data-embedauthorid="3889" data-embedcontent="" data-embedid="embed9159437912" src="https://academy.hsoub.com/files/33-%D8%AF%D9%84%D9%8A%D9%84-javascript-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84/?do=embed" style="overflow: hidden; height: 469px; max-width: 500px; margin:auto"></iframe>

<p>
	أما إذا كنت تشعر بالتشتت من الدراسة الذاتية وتريد مصدرًا تعليميًا منظمًا يساعدك على تعلم كل ما تحتاجه ضمن خطة منظمة ويوفر لك الدعم والتوجيه اللازم، ويجيبك على أي تساؤلات أو مشكلات تواجهك، ويرشدك لتطوير تطبيقات فعلية تضيفها لمعرض أعمالك وتعزز فرصتك في الحصول على عمل فقد وفرت أكاديمية حسوب <a href="https://academy.hsoub.com/courses/javascript-application-development/" rel="">دورة تطوير التطبيقات باستخدام JavaScript</a> التي تبدأ معك من تعلم أساسيات لغة جافا سكريبت بأحدث إصدارتها وتوفر لك عدة مسارات مثل مسار لتعلم استخدام مكتبة React.js وبناء تطبيق إدارة مهام باستخدامها وأساسيات إطار React Native وتطوير العديد من التطبيقات باستخدامه مثل تطوير تطبيق جوال طبيبي للتواصل بين الأطباء والمرضى إلى جانب العديد من المسارات الأخرى التي تمكنك من تطوير تطبيقات جوال بأساليب متنوعة.
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="480" id="ips_uid_3582_7" src="https://academy.hsoub.com/applications/core/interface/index.html" title="دورة تطوير التطبيقات باستخدام JavaScript - أكاديمية حسوب" width="853" data-embed-src="https://www.youtube.com/embed/T6CnMmvF4e0"></iframe>
</p>

<h2 id="-2">
	الخلاصة
</h2>

<p>
	بهذا نكون قد وصلنا لنهاية مقالنا الذي نرجو أن يكون قد عرفك على كل ما تود معرفته عن إطار ريآكت نيتف لتطوير تطبيقات الجوال بلغة البرمجة جافا سكريبت، وبينا لك من خلاله أبرز مميزات وعيوب هذا الإطار وخطوات ومصادر مفيدة لتعلمه وختمنا المقال بأكثر الأسئلة الشائعة حوله.
</p>

<p>
	هل أنت مهتم ببناء تطبيقات الهواتف المحمولة ولديك أي تساؤل حول استخدامات React Native في هذا المجال؟ لا تتردد في طرح سؤالك في قسم التعليقات أسفل المقال.
</p>

<h2 id="-3">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-react-native-r1264/" rel="">مدخل إلى React Native</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%B5%D8%A7%D8%AF%D8%B1-%D8%AA%D8%B9%D9%84%D9%85-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-react-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1575/" rel="">مصادر تعلم مكتبة React لبناء تطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A3%D8%B7%D8%B1-%D8%B9%D9%85%D9%84-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1567/" rel="">مقدمة إلى أطر عمل تطوير الويب من طرف العميل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82/" rel="">خطوات برمجة تطبيق للمبتدئين</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D8%AD%D8%B1%D9%8A%D9%83-%D9%81%D9%8A-react-native-r925/" rel="">مدخل إلى التحريك في React Native</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D9%86%D8%AF%D8%B1%D9%88%D9%8A%D8%AF-r1802/" rel="">برمجة تطبيقات الأندرويد</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2199</guid><pubDate>Wed, 27 Dec 2023 07:43:00 +0000</pubDate></item><item><title>&#x627;&#x62E;&#x62A;&#x628;&#x627;&#x631; &#x648;&#x62A;&#x648;&#x633;&#x64A;&#x639; &#x62A;&#x637;&#x628;&#x64A;&#x642; React Native</title><link>https://academy.hsoub.com/programming/javascript/react/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D9%88%D8%AA%D9%88%D8%B3%D9%8A%D8%B9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-native-r1678/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/Testing-and-extending-our-application.png.fc644de4d2dfc552aadc035da8e66eaf.png" /></p>
<p>
	لقد حان الوقت لتوسيع مشروعنا بعد أن أسسنا له قاعدةً معرفيةً جيدة، وسنسخّر لهذه المهمة كل إمكانيات <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-react-native-r1264/" rel="">React Native</a> التي تعلمناها حتى الآن. سنغطي بالإضافة إلى توسيع المشروع نواحٍ جديدة، مثل الاختبارات والموارد الإضافية.
</p>

<h2>
	اختبار تطبيقات React Native
</h2>

<p>
	سنحتاج إلى إطار عمل للاختبارات عند البدء باختبار أي نوع من الشيفرة، لتنفيذ مجموعة من الحالات المختبرة، والتدقيق في نتائجها. فلاختبار تطبيق <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">جافا سكربت JavaScript</a> سنجد أن إطار العمل <a href="https://jestjs.io/" rel="external nofollow">Jest</a> هو الأكثر شعبية لأداء المهمة. ولاختبار تطبيقات React Native المبنية على المنصة Expo باستخدام Jest، ستزودنا Expo بمجموعة من إعدادات تهيئة على هيئة مجموعة جاهزة تدعى <a href="https://github.com/expo/expo/tree/master/packages/jest-expo" rel="external nofollow">jest-expo</a>. ولكي نستخدم المدقق ESLint في ملف اختبار Jest، سنحتاج إلى الإضافة <a href="https://www.npmjs.com/package/eslint-plugin-jest" rel="external nofollow">eslint-plugin-jest</a> الخاصة به. لنبدأ إذًا بتثبيت الحزم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_9" style=""><span class="pln">npm install </span><span class="pun">--</span><span class="pln">save</span><span class="pun">-</span><span class="pln">dev jest jest</span><span class="pun">-</span><span class="pln">expo eslint</span><span class="pun">-</span><span class="pln">plugin</span><span class="pun">-</span><span class="pln">jest</span></pre>

<p>
	لاستخدام المجموعة jest-expo في Jest، لا بدّ من إضافة إعدادات التهيئة التالية في الملف "package.json"، مع سكربت الاختبار:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_11" style=""><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// other scripts...</span><span class="pln">
    </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"jest"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"jest"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">    
      </span><span class="str">"preset"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"jest-expo"</span><span class="pun">,</span><span class="pln">    
      </span><span class="str">"transform"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">      
          </span><span class="str">"^.+\\.jsx?$"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"babel-jest"</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">    
      </span><span class="str">"transformIgnorePatterns"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
     </span><span class="str">"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg|react-router-native)"</span><span class="pln">

      </span><span class="pun">]</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يطلب الخيار <code>transform</code> من Jest تحويل شيفرة الملفين "js." و"jsx." باستخدام مصرّف <a href="https://babeljs.io/" rel="external nofollow">Babel</a>. بينما يُستخدم الخيار transformIgnorePatterns لتجاهل مجلدات محددة ضمن المجلد "node_modules" أثناء تحويل الملفات. وتتطابق تقريبًا إعدادات Jest هذه مع تلك المقترحة في <a href="https://docs.expo.io/guides/testing-with-jest" rel="external nofollow">توثيق Expo</a>.
</p>

<p>
	لاستخدام الإضافة eslint-plugin-jest، لا بدّ من وضعها ضمن مصفوفة الإضافات والموسِّعات في الملف "eslintrc.".
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_13" style=""><span class="pun">{</span><span class="pln">
  </span><span class="str">"plugins"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"react"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"react-native"</span><span class="pun">],</span><span class="pln">
  </span><span class="str">"settings"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"react"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"detect"</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">

 </span><span class="str">"extends"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"eslint:recommended"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"plugin:react/recommended"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"plugin:jest/recommended"</span><span class="pun">],</span><span class="pln">
 </span><span class="str">"parser"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"@babel/eslint-parser"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"env"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"react-native/react-native"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"rules"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"react/prop-types"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"off"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"react/react-in-jsx-scope"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"off"</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وللتأكد من نجاح التهيئة، أنشئ المجلد "<strong>tests</strong>" في المجلد "src"، ثم أنشئ ضمنه الملف "example.js" وضع فيه الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_15" style=""><span class="pln">describe</span><span class="pun">(</span><span class="str">'Example'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  it</span><span class="pun">(</span><span class="str">'works'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    expect</span><span class="pun">(</span><span class="lit">1</span><span class="pun">).</span><span class="pln">toBe</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	لننفذ الآن هذا المثال من خلال الأمر: <code>npm test</code>. ينبغي أن يشير خرج العملية إلى نجاح الاختبار المتواجد في الملف "src/<strong>tests</strong>/example.js".
</p>

<h2>
	تنظيم الاختبارات
</h2>

<p>
	تقتضي إحدى طرق تنظيم ملفات الاختبارات بوضعها في مجلد وحيد يُدعى "<strong>tests</strong>". ويفضّل عند استخدام هذه الطريقة وضع ملفات الاختبار ضمن المجلدات الفرعية الملائمة كما نفعل مع ملفات الشيفرة، إذ ستُوضع الاختبارات التي تتعلق بالمكوّنات مثلًا في المجلّد "components"، وتلك التي تتعلق بالخدمات ستُوضع في المجلّد "utils"، وهكذا. سينتج عن هذا التنظيم الهيكلية التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_17" style=""><span class="pln">src</span><span class="pun">/</span><span class="pln">
  __tests__</span><span class="pun">/</span><span class="pln">
    components</span><span class="pun">/</span><span class="pln">
      </span><span class="typ">AppBar</span><span class="pun">.</span><span class="pln">js
      </span><span class="typ">RepositoryList</span><span class="pun">.</span><span class="pln">js
      </span><span class="pun">...</span><span class="pln">
    utils</span><span class="pun">/</span><span class="pln">
      authStorage</span><span class="pun">.</span><span class="pln">js
      </span><span class="pun">...</span><span class="pln">
    </span><span class="pun">...</span></pre>

<p>
	أما الطريقة الأخرى فهي وضع ملف الاختبار بجانب ملف الشيفرة الذي سيستخدمه. ويعني ذلك أننا سنضع الملف الذي يتضمن اختبارات للمكوّن <code>AppBar</code> مثلًا في نفس المجلد الذي يحتوي شيفرة هذا المكوّن. وستنتج عن هذه الطريقة في التنظيم الهيكلية التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_19" style=""><span class="pln">src</span><span class="pun">/</span><span class="pln">
  components</span><span class="pun">/</span><span class="pln">
    </span><span class="typ">AppBar</span><span class="pun">/</span><span class="pln">
      </span><span class="typ">AppBar</span><span class="pun">.</span><span class="pln">test</span><span class="pun">.</span><span class="pln">jsx
      index</span><span class="pun">.</span><span class="pln">jsx
    </span><span class="pun">...</span><span class="pln">
  </span><span class="pun">...</span></pre>

<p>
	شيفرة المكوّن في المثال السابق موجودةٌ في الملف "index.jsx"، بينما ستجد الاختبارات في الملف "AppBar.test.jsx". ولكي تجد بسهولة الملفات التي تحتوي الاختبارات، عليك أن تضعها في المجلد "<strong>tests</strong>" أو في ملفات تحمل إحدى اللاحقتين "test." أو "spec."، أو أن <a href="https://jestjs.io/docs/en/configuration#testmatch-arraystring" rel="external nofollow">تهيئ يدويًا</a> الأنماط العامة global patterns.
</p>

<h2>
	اختبار المكونات
</h2>

<p>
	بعد أن ضبطنا إعدادات Jest وجربناها بتنفيذ مثال بسيط، حان الوقت لنتعرف على كيفية اختبار المكوّنات. يتطلب اختبار المكوّنات، كما نعلم، طريقة لتفسير الخرج الناتج عن تصيير المكوّن ومحاكاة عملية معالجة الأحداث المختلفة مثل الضغط على زر. ولحسن الحظ تتوافر عائلة من <a href="https://testing-library.com/docs/intro" rel="external nofollow">مكتبات الاختبار</a> تؤمن مكتبات لاختبار مكوًنات واجهة المكوّن في منصات عدة. وتتشارك جميع هذه المكتبات بواجهة برمجية واحدة <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel=""><abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> لاختبار مكونات واجهة المستخدم بأسلوب يركّز على المستخدم.
</p>

<p>
	تعرّفنا في <a href="https://academy.hsoub.com/programming/javascript/react/%D8%AA%D8%B3%D8%AC%D9%8A%D9%84-%D8%A7%D9%84%D8%AF%D8%AE%D9%88%D9%84-%D9%81%D9%8A-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A3%D9%85%D8%A7%D9%85%D9%8A%D8%A9-r1136/" rel="">القسم 5</a> على إحدى هذه المكتبات وهي <a href="https://testing-library.com/docs/react-testing-library/intro" rel="external nofollow">React Testing Library</a>. لكن لسوء الحظ فهذه المكتبة مخصصة فقط لاختبار تطبيقات الويب المبنية باستخدام React. لكن بالطبع هناك مقابل لها في React Native وهي المكتبة <a href="https://callstack.github.io/react-native-testing-library/" rel="external nofollow">React Native Testing Library</a> التي سنستخدمها في اختبار مكوّنات تطبيقات React Native. وكما ذكرنا فإن جميع هذه المكتبات تتشارك بنفس الواجهة البرمجية، ولن تضطر إلى تعلّم الكثير من المفاهيم الجديدة. بالإضافة إلى هذه المكتبة، سنحتاج إلى مجموعة من مُطابقات Jest مخصصة للمكتبة مثل <code>toHaveTextContent</code> و <code>toHaveProp</code>. تزوّدنا المكتبة <a href="https://github.com/testing-library/jest-native" rel="external nofollow">jest-native</a> بهذه المُطابقات، لذلك لا بدّ أولًا من تثبيت هذه الحزم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_26" style=""><span class="pln">npm install </span><span class="pun">--</span><span class="pln">save</span><span class="pun">-</span><span class="pln">dev react</span><span class="pun">-</span><span class="pln">test</span><span class="pun">-</span><span class="pln">renderer@17</span><span class="pun">.</span><span class="lit">0.1</span><span class="pln"> </span><span class="lit">@testing</span><span class="pun">-</span><span class="pln">library</span><span class="pun">/</span><span class="pln">react</span><span class="pun">-</span><span class="pln">native </span><span class="lit">@testing</span><span class="pun">-</span><span class="pln">library</span><span class="pun">/</span><span class="pln">jest</span><span class="pun">-</span><span class="pln">native</span></pre>

<p>
	<strong>ملاحظة:</strong> إذا واجهت مشاكل في اعتمادية النظير peer، فتأكد من مطابقة إصدار react-test-renderer مع نسخة react للمشروع في أمر <code>npm install</code> المذكور أعلاه. يمكنك التحقق من إصدار react من خلال تنفيذ الأمر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_29" style=""><span class="pln">npm list react </span><span class="pun">--</span><span class="pln">depth</span><span class="pun">=</span><span class="lit">0</span></pre>

<p>
	وفي حال فشل التثبيت بسبب مشكلات اعتمادية النظير، فحاول مرةً أخرى باستخدام الراية <code>‎--legacy-peer-deps</code> في الأمر <code>npm install</code>.
</p>

<p>
	لنتمكن من استخدام هذه المُطابقات، علينا توسيع الكائن <code>expect</code> العائد للمكتبة Jest. ويجري تنفيذ ذلك باستخدام ملف إعداد عام. لذلك أنشئ الملف "setupTests.js" في المجلد الجذري لمشروعك، وهو نفسه المجلد الذ++ي يحتوي الملف "package.json". أضف الشيفرة التالية إلى هذا الملف:
</p>

<pre class="ipsCode">import '@testing-library/jest-native/extend-expect';
</pre>

<p>
	هيئ الملف السابق على انه ملف إعداد ضمن أوامر تهيئة Jest الموجودة في الملف "package.json":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_22" style=""><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  </span><span class="str">"jest"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"preset"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"jest-expo"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"transform"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"^.+\\.jsx?$"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"babel-jest"</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    </span><span class="str">"transformIgnorePatterns"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
      </span><span class="str">"node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|@sentry/.*|react-router-native)"</span><span class="pln">
    </span><span class="pun">],</span><span class="pln">
    </span><span class="str">"setupFilesAfterEnv"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"&lt;rootDir&gt;/setupTests.js"</span><span class="pun">]</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تعتمد المكتبة على مفهومين أساسيين هما <a href="https://callstack.github.io/react-native-testing-library/docs/api-queries" rel="external nofollow">الاستعلامات (query)</a> و<a href="https://callstack.github.io/react-native-testing-library/docs/api#fireevent" rel="external nofollow">إطلاق الأحداث (fire event)</a>؛ إذ تُستخدم الاستعلامات لاستخلاص مجموعة من العقد من المكوّن الذي يُصيّر بواسطة الدالة <a href="https://callstack.github.io/react-native-testing-library/docs/api#render" rel="external nofollow">render</a>، ونستفيد منها في اختبار وجود عنصر أو كائن ما، مثل نص معين، ضمن المكوّن المُصيَّر. يمكنك تحديد العقد باستخدام الخاصية <code>testID</code> ثم الاستعلام عنها باستخدام الدالة <a href="https://callstack.github.io/react-native-testing-library/docs/api-queries#bytestid" rel="external nofollow">getByTestId</a>. تقبل جميع المكوّنات البنيوية في الخاصية <code>testID</code>. وإليك مثالًا عن كيفية استخدام الاستعلامات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_24" style=""><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Text</span><span class="pun">,</span><span class="pln"> </span><span class="typ">View</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> render </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@testing-library/react-native'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Greeting</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> name </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="typ">Hello</span><span class="pln"> </span><span class="pun">{</span><span class="pln">name</span><span class="pun">}!&lt;/</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

describe</span><span class="pun">(</span><span class="str">'Greeting'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  it</span><span class="pun">(</span><span class="str">'renders a greeting message based on the name prop'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> debug</span><span class="pun">,</span><span class="pln"> getByText </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> render</span><span class="pun">(&lt;</span><span class="typ">Greeting</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"Kalle"</span><span class="pln"> </span><span class="pun">/&gt;);</span><span class="pln">

    debug</span><span class="pun">();</span><span class="pln">

    expect</span><span class="pun">(</span><span class="pln">getByText</span><span class="pun">(</span><span class="str">'Hello Kalle!'</span><span class="pun">)).</span><span class="pln">toBeDefined</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تعيد الدالة <code>render</code> الاستعلامات ودوال مساعدة أخرى مثل الدالة <a href="https://callstack.github.io/react-native-testing-library/docs/api#debug" rel="external nofollow">debug</a> التي تطبع شجرة React بطريقة واضحة بالنسبة للمستخدم. استخدم هذه الدالة إن كنت غير متأكد من شكل المكوّن الذي صيَّرته الدالة <code>render</code>. يمكننا أن نحصل على العقدة <code>Text</code> التي تحتوي نصًا محددًا عن طريق الدالة <code>getByText</code>. وللاطلاع على كل الاستعلامات المتاحة، راجع <a href="https://github.com/testing-library/jest-native#matchers" rel="external nofollow">توثيق</a> المكتبة React Native Testing. يُستخدم المُطابق <code>toHaveTextContent</code> للتأكد من أن المحتوى النصي للعقدة صحيح. وللاطلاع كذلك على كل المُطابقات الخاصة بالمكتبة React Native، راجع <a href="https://github.com/testing-library/jest-native#matchers" rel="external nofollow">توثيق</a> jest-native. وتذكر أنك ستجد معلومات عن كل مُطابقٍ عام في <a href="https://jestjs.io/docs/en/expect" rel="external nofollow">توثيق Jest</a>.
</p>

<p>
	أما المفهوم الثاني الذي ترتكز عليه المكتبة React Native Testing في عملها هو إطلاق الأحداث firing events، إذ يمكننا إطلاق حدث ضمن عقدة معينة باستخدام توابع الكائن <a href="https://callstack.github.io/react-native-testing-library/docs/api#fireevent" rel="external nofollow">fireEvent</a>. سنستفيد من ذلك على سبيل المثال في طباعة نص ضمن حقل نصي أو على زر. وإليك مثال عن اختبار تسليم نموذج بسيط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_32" style=""><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useState </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Text</span><span class="pun">,</span><span class="pln"> </span><span class="typ">TextInput</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Pressable</span><span class="pun">,</span><span class="pln"> </span><span class="typ">View</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> render</span><span class="pun">,</span><span class="pln"> fireEvent </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@testing-library/react-native'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Form</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> onSubmit </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">username</span><span class="pun">,</span><span class="pln"> setUsername</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">''</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">password</span><span class="pun">,</span><span class="pln"> setPassword</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">''</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> handleSubmit </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    onSubmit</span><span class="pun">({</span><span class="pln"> username</span><span class="pun">,</span><span class="pln"> password </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">TextInput</span><span class="pln">
          value</span><span class="pun">={</span><span class="pln">username</span><span class="pun">}</span><span class="pln">
          onChangeText</span><span class="pun">={(</span><span class="pln">text</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setUsername</span><span class="pun">(</span><span class="pln">text</span><span class="pun">)}</span><span class="pln">
          placeholder</span><span class="pun">=</span><span class="str">"Username"</span><span class="pln">
        </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">TextInput</span><span class="pln">
          value</span><span class="pun">={</span><span class="pln">password</span><span class="pun">}</span><span class="pln">
          onChangeText</span><span class="pun">={(</span><span class="pln">text</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setPassword</span><span class="pun">(</span><span class="pln">text</span><span class="pun">)}</span><span class="pln">
          placeholder</span><span class="pun">=</span><span class="str">"Password"</span><span class="pln">
        </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">Pressable</span><span class="pln"> onPress</span><span class="pun">={</span><span class="pln">handleSubmit</span><span class="pun">}&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="typ">Submit</span><span class="pun">&lt;/</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="typ">Pressable</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

describe</span><span class="pun">(</span><span class="str">'Form'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  it</span><span class="pun">(</span><span class="str">'calls function provided by onSubmit prop after pressing the submit button'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> onSubmit </span><span class="pun">=</span><span class="pln"> jest</span><span class="pun">.</span><span class="pln">fn</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> getByPlaceholderText</span><span class="pun">,</span><span class="pln"> getByText </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> render</span><span class="pun">(&lt;</span><span class="typ">Form</span><span class="pln"> onSubmit</span><span class="pun">={</span><span class="pln">onSubmit</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;);</span><span class="pln">

    fireEvent</span><span class="pun">.</span><span class="pln">changeText</span><span class="pun">(</span><span class="pln">getByPlaceholderText</span><span class="pun">(</span><span class="str">'Username'</span><span class="pun">),</span><span class="pln"> </span><span class="str">'kalle'</span><span class="pun">);</span><span class="pln">
    fireEvent</span><span class="pun">.</span><span class="pln">changeText</span><span class="pun">(</span><span class="pln">getByPlaceholderText</span><span class="pun">(</span><span class="str">'Password'</span><span class="pun">),</span><span class="pln"> </span><span class="str">'password'</span><span class="pun">);</span><span class="pln">
    fireEvent</span><span class="pun">.</span><span class="pln">press</span><span class="pun">(</span><span class="pln">getByText</span><span class="pun">(</span><span class="str">'Submit'</span><span class="pun">));</span><span class="pln">

    expect</span><span class="pun">(</span><span class="pln">onSubmit</span><span class="pun">).</span><span class="pln">toHaveBeenCalledTimes</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// تحوي أول وسيط من أول استدعاء onSubmit.mock.calls[0][0]</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">onSubmit</span><span class="pun">.</span><span class="pln">mock</span><span class="pun">.</span><span class="pln">calls</span><span class="pun">[</span><span class="lit">0</span><span class="pun">][</span><span class="lit">0</span><span class="pun">]).</span><span class="pln">toEqual</span><span class="pun">({</span><span class="pln">
      username</span><span class="pun">:</span><span class="pln"> </span><span class="str">'kalle'</span><span class="pun">,</span><span class="pln">
      password</span><span class="pun">:</span><span class="pln"> </span><span class="str">'password'</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نريد في هذا الاختبار أن نتأكد من أنّ الدالة <code>onSubmit</code> ستُستدعى على نحوٍ صحيح بعد أن تُملأ حقول النموذج باستخدام التابع <code>fireEvent.changeText</code> ويُضغط على الزر باستخدام التابع <code>fireEvent.press</code>. ولتحري إذا ما استُدعيت الدالة <code>onSubmit</code> وبأية وسطاء، يمكن استخدام الدالة المقلّدة <a href="https://jestjs.io/docs/en/mock-function-api" rel="external nofollow">mock function</a>؛ والدوال المقلدة هي دوال ذات سلوك مبرمج مسبقًا كأن تعيد قيمةً محددة. كما يمكننا تحديد توقّعات expectations لهذه الدوال، فمثلًا "توقّع أن الدالة المقلّدة قد استدعيت مرةً واحدة". يمكنك الاطلاع على قائمة بكل التوقعات المتاحة في <a href="https://jestjs.io/docs/en/expect" rel="external nofollow">توثيق توقعات Jest</a>.
</p>

<p>
	عدّل وجرّب في تلك الأمثلة السابقة، وأضف اختبارات جديدة في المجلد "<strong>tests</strong> "، بحيث تتأكد من فهمك للأفكار السابقة قبل الغوص أعمق في موضوع الاختبارات.
</p>

<h2>
	التعامل مع الاعتماديات أثناء الاختبارات
</h2>

<p>
	يُعد اختبار المكوّنات سهلًا نوعًا ما لأنها صرفة بشكلٍ أو بآخر. فلا تعتمد المكوّنات الصرّفة على التأثيرات الجانبية side effects، مثل طلبات الشبكة أو استخدام واجهات برمجية أصيلة، مثل AsyncStorage. فالمكوّن <code>Form</code> ليس صرفًا بقدر المكون <code>Greeting</code>، لأن تغيرات حالته قد تُعد أثرًا جانبيًا. لكن الاختبارات تبقى سهلةً مع ذلك.
</p>

<p>
	لنلقِ الآن نظرةً على استراتيجية لاختبار المكوّنات مع الآثار الجانبية، وسنختار على سبيل المثال المكوّن <code>RepositoryList</code> من تطبيقنا. لهذا المكوّن حاليًا أثر جانبي واحد وهو استعلام GraphQl الذي يحضر قائمة بالمستودعات المقيّمة. يُستخدم المكوّن حاليًا كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_34" style=""><span class="kwd">const</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> repositories </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useRepositories</span><span class="pun">();</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> repositoryNodes </span><span class="pun">=</span><span class="pln"> repositories
    </span><span class="pun">?</span><span class="pln"> repositories</span><span class="pun">.</span><span class="pln">edges</span><span class="pun">.</span><span class="pln">map</span><span class="pun">((</span><span class="pln">edge</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> edge</span><span class="pun">.</span><span class="pln">node</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">:</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">FlatList</span><span class="pln">
      data</span><span class="pun">={</span><span class="pln">repositoryNodes</span><span class="pun">}</span><span class="pln">
      </span><span class="com">// ...</span><span class="pln">
    </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pun">;</span></pre>

<p>
	ينتج التأثير الجانبي عن استخدام الخطاف <code>useRepositories</code> الذي يرسل استعلام GraphQL. هناك عدة طرق لاختبار المكوّن، إحدى هذه الطرق هي تقليد استجابات المكتبة Apollo Client كما هو مشروح في <a href="https://www.apollographql.com/docs/react/development-testing/testing/" rel="external nofollow">توثيقها</a>. أما الطريقة الأبسط هي افتراض أن المكوّن يعمل كما نتوقع (ويُفضل من خلال اختباره)، ثم استخلاص الشيفرة "الصرفة" له ووضعها في مكوّن آخر مثل <code>RepositoryListContainer</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_36" style=""><span class="kwd">export</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">RepositoryListContainer</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> repositories </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> repositoryNodes </span><span class="pun">=</span><span class="pln"> repositories
    </span><span class="pun">?</span><span class="pln"> repositories</span><span class="pun">.</span><span class="pln">edges</span><span class="pun">.</span><span class="pln">map</span><span class="pun">((</span><span class="pln">edge</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> edge</span><span class="pun">.</span><span class="pln">node</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">:</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">FlatList</span><span class="pln">
      data</span><span class="pun">={</span><span class="pln">repositoryNodes</span><span class="pun">}</span><span class="pln">
      </span><span class="com">// ...</span><span class="pln">
    </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> repositories </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useRepositories</span><span class="pun">();</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">RepositoryListContainer</span><span class="pln"> repositories</span><span class="pun">={</span><span class="pln">repositories</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pun">;</span></pre>

<p>
	يحتوي المكوّن الآن التأثيرات الجانبية فقط وتنفيذه سهل. يمكن اختبار المكوّن <code>RepositoryListContainer</code> بتمرير بيانات عن مستودع من القائمة من خلال الخاصية <code>repositories</code>، والتأكد أنّ معلومات المحتوى المُصيَّر صحيحة.
</p>

<h2>
	التمرينان 10.17 - 10.18
</h2>

<h3>
	10.17 اختبار قائمة المستودعات المقيمة
</h3>

<p>
	أنجز اختبارًا يتأكد أن المكوّن <code>RepositoryListContainer</code> سيصيِّر بصورةٍ صحيحة كلًا من: اسم المستودع ووصفه واللغة وعدد التشعبات وعدد النجوم ومعدل التقييم وعدد التقييمات. تذكر أنه بالإمكان الاستفادة من المطابق <a href="https://github.com/testing-library/jest-native#tohavetextcontent" rel="external nofollow">toHaveTextContent</a> في التحقق من احتواء العقدة على سلسلة نصية محددة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_38" style=""><span class="kwd">const</span><span class="pln"> </span><span class="typ">RepositoryItem</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="com">/* ... */</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pln"> testID</span><span class="pun">=</span><span class="str">"repositoryItem"</span><span class="pln"> </span><span class="pun">{</span><span class="com">/* ... */</span><span class="pun">}&gt;</span><span class="pln">
      </span><span class="pun">{</span><span class="com">/* ... */</span><span class="pun">}</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	كما يمكنك استخدام الاستعلام <a href="https://callstack.github.io/react-native-testing-library/docs/api-queries#getallby" rel="external nofollow">getAllByTestId</a> للحصول على كل العقد التي تمتلك قيمة محددة للخاصية <code>testID</code> على هيئة مصفوفة، وإن كنت غير متأكد من المكوّن الذي صُيَّر، استفد من الدالة <a href="https://callstack.github.io/react-native-testing-library/docs/api#debug" rel="external nofollow">debug</a> في تفكيك نتيجة التصيير.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_40" style=""><span class="kwd">const</span><span class="pln"> repositoryItems </span><span class="pun">=</span><span class="pln"> getAllByTestId</span><span class="pun">(</span><span class="str">'repositoryItem'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">firstRepositoryItem</span><span class="pun">,</span><span class="pln"> secondRepositoryItem</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> repositoryItems</span><span class="pun">;</span><span class="pln">

</span><span class="com">// توقع شيئًا من عنصر المستودع الأول والثاني</span></pre>

<p>
	استخدم الشيفرة التالية أساسًا لاختبارك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_42" style=""><span class="pln">describe</span><span class="pun">(</span><span class="str">'RepositoryList'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  describe</span><span class="pun">(</span><span class="str">'RepositoryListContainer'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">(</span><span class="str">'renders repository information correctly'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> repositories </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        pageInfo</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          totalCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln">
          hasNextPage</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
          endCursor</span><span class="pun">:</span><span class="pln">
            </span><span class="str">'WyJhc3luYy1saWJyYXJ5LnJlYWN0LWFzeW5jIiwxNTg4NjU2NzUwMDc2XQ=='</span><span class="pun">,</span><span class="pln">
          startCursor</span><span class="pun">:</span><span class="pln"> </span><span class="str">'WyJqYXJlZHBhbG1lci5mb3JtaWsiLDE1ODg2NjAzNTAwNzZd'</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">},</span><span class="pln">
        edges</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
          </span><span class="pun">{</span><span class="pln">
            node</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
              id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'jaredpalmer.formik'</span><span class="pun">,</span><span class="pln">
              fullName</span><span class="pun">:</span><span class="pln"> </span><span class="str">'jaredpalmer/formik'</span><span class="pun">,</span><span class="pln">
              description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Build forms in React, without the tears'</span><span class="pun">,</span><span class="pln">
              language</span><span class="pun">:</span><span class="pln"> </span><span class="str">'TypeScript'</span><span class="pun">,</span><span class="pln">
              forksCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1619</span><span class="pun">,</span><span class="pln">
              stargazersCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">21856</span><span class="pun">,</span><span class="pln">
              ratingAverage</span><span class="pun">:</span><span class="pln"> </span><span class="lit">88</span><span class="pun">,</span><span class="pln">
              reviewCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln">
              ownerAvatarUrl</span><span class="pun">:</span><span class="pln">
                </span><span class="str">'https://avatars2.githubusercontent.com/u/4060187?v=4'</span><span class="pun">,</span><span class="pln">
            </span><span class="pun">},</span><span class="pln">
            cursor</span><span class="pun">:</span><span class="pln"> </span><span class="str">'WyJqYXJlZHBhbG1lci5mb3JtaWsiLDE1ODg2NjAzNTAwNzZd'</span><span class="pun">,</span><span class="pln">
          </span><span class="pun">},</span><span class="pln">
          </span><span class="pun">{</span><span class="pln">
            node</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
              id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'async-library.react-async'</span><span class="pun">,</span><span class="pln">
              fullName</span><span class="pun">:</span><span class="pln"> </span><span class="str">'async-library/react-async'</span><span class="pun">,</span><span class="pln">
              description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Flexible promise-based React data loader'</span><span class="pun">,</span><span class="pln">
              language</span><span class="pun">:</span><span class="pln"> </span><span class="str">'JavaScript'</span><span class="pun">,</span><span class="pln">
              forksCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">69</span><span class="pun">,</span><span class="pln">
              stargazersCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1760</span><span class="pun">,</span><span class="pln">
              ratingAverage</span><span class="pun">:</span><span class="pln"> </span><span class="lit">72</span><span class="pun">,</span><span class="pln">
              reviewCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln">
              ownerAvatarUrl</span><span class="pun">:</span><span class="pln">
                </span><span class="str">'https://avatars1.githubusercontent.com/u/54310907?v=4'</span><span class="pun">,</span><span class="pln">
            </span><span class="pun">},</span><span class="pln">
            cursor</span><span class="pun">:</span><span class="pln">
              </span><span class="str">'WyJhc3luYy1saWJyYXJ5LnJlYWN0LWFzeW5jIiwxNTg4NjU2NzUwMDc2XQ=='</span><span class="pun">,</span><span class="pln">
          </span><span class="pun">},</span><span class="pln">
        </span><span class="pun">],</span><span class="pln">
      </span><span class="pun">};</span><span class="pln">

      </span><span class="com">// أضف شيفرة اختبارك هنا</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يمكن أن تضع ملف الاختبار أينما تشاء، لكن من الأفضل أن تتبع إحدى الطرق التي ذكرناها في تنظيم ملفات الاختبار. استخدم المتغير <code>repositories</code> مثل مصدر للبيانات في الاختبار، ولا حاجة لتغيير قيمته. تحتوي البيانات على مستودعين، فعليك بالتالي التحقق من وجود المعلومات في كليهما.
</p>

<h3>
	10.18 اختبار نموذج تسجيل الدخول
</h3>

<p>
	أنجز اختبارًا يتحقق أن الدالة <code>onSubmit</code> ستُستدعى بوسطاء مناسبين عندما يُملأ حقلي اسم المستخدم وكلمة المرور في النموذج ويُضغط على زر الإرسال. ينبغي أن يكون الوسيط الأول كائنًا يمثّل قيم النموذج، ويمكنك إهمال بقية وسطاء الدالة. تذكر أنك قد تستعمل توابع <a href="https://callstack.github.io/react-native-testing-library/docs/api#fireevent" rel="external nofollow">fireEvent</a> لتنفيذ أحداث و<a href="https://jestjs.io/docs/en/mock-function-api" rel="external nofollow">دوال مقلِّدة</a> للتحقق من استدعاء معالج الحدث <code>onSubmit</code> أو لا.
</p>

<p>
	لا حاجة لاختبار أي شيفرة متعلقة بالمكتبة Apollo Client أو AsyncStorage والموجودة في الخطاف <code>useSignIn</code>. وكما فعلنا في التمرين السابق، استخلص الشيفرة الصرفة وضعها في مكوّن خاص واختبره.
</p>

<p>
	انتبه إلى إرسالات نماذج Formik فهي غير متزامنة، فلا تتوقع استدعاء الدالة <code>onSubmit</code> مباشرة بعد الضغط على زر الإرسال. يمكنك الالتفاف على المشكلة بجعل دالة الاختبار دالة غير متزامنة باستخدام التعليمة <code>Async</code> واستخدام الدالة المساعدة <a href="https://callstack.github.io/react-native-testing-library/docs/api#waitfor" rel="external nofollow">waitFor</a> العائدة للمكتبة React Native Testing. كما يمكنك يمكن أن تستخدم الدالة <a href="https://callstack.github.io/react-native-testing-library/docs/api#waitfor" rel="external nofollow">waitFor</a> لانتظار تحقق التوقعات. فإن لم يتحقق التوقع خلال فترة محددة، سترمي الدالة خطأً. إليك مثالًا يشير إلى أسلوب استخدام هذه الدالة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_44" style=""><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> render</span><span class="pun">,</span><span class="pln"> fireEvent</span><span class="pun">,</span><span class="pln"> waitFor </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@testing-library/react-native'</span><span class="pun">;</span><span class="pln">
</span><span class="com">// ...</span><span class="pln">

describe</span><span class="pun">(</span><span class="str">'SignIn'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  describe</span><span class="pun">(</span><span class="str">'SignInContainer'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    it</span><span class="pun">(</span><span class="str">'calls onSubmit function with correct arguments when a valid form is submitted'</span><span class="pun">,</span><span class="pln"> async </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">//صيّر المكوّن ثم املأ الحقول النصية ثم اضغط زر الإرسال</span><span class="pln">
      await waitFor</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// توقع أن تُستدعى دالة الإرسال مرة واحد وأن يكون الوسيط الأول صحيحًا</span><span class="pln">

      </span><span class="pun">});</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<h2>
	توسيع التطبيق
</h2>

<p>
	سنبدأ باستخدام كل ما تعلمناه في توسيع تطبيقنا، فلا يزال التطبيق يفتقر لبعض النواحي، مثل عرض معلومات مستودع وتسجيل مستخدمين جدد. سنركز في التمارين القادمة على هذه النقاط.
</p>

<h2>
	التمرينات 10.19 - 10.24
</h2>

<h3>
	10.19: واجهة لعرض مستودع واحد
</h3>

<p>
	أنجز واجهةً لعرض معلومات عن مستودع واحد، تتضمن نفس المعلومات التي تُعرض في قائمة المستودعات بالإضافة إلى زر لفتح المستودع في غيت هب Github. من الجيد أن تجد طريقةً لإعادة استخدام المكونين <code>RepositoryItem</code> و <code>RepositoryList</code> وأن تعرض الزر الذي أشرنا إليه بناء على خاصية محددة تعيد قيمة منطقية مثلًا.
</p>

<p>
	ستجد عنوان المستودع ضمن الحقل<code>url</code> العائد للنوع "Repository" في تخطيط GraphQL. تستطيع إحضار مستودع واحد من خادم Apollo بإجراء الاستعلام <code>repository</code>. يُمرَّر إلى هذا الاستعلام وسيط واحد وهو المعرّف المميز <code>id</code> للمستودع. إليك مثالًا عن استخدام الاستعلام <code>repository</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_46" style=""><span class="pun">{</span><span class="pln">
  repository</span><span class="pun">(</span><span class="pln">id</span><span class="pun">:</span><span class="pln"> </span><span class="str">"jaredpalmer.formik"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    id
    fullName
    url
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	اختبر استعلامك كما هو معتاد في أرضية عمل Apollo قبل استخدامه في التطبيق. إن لم تكن على دراية بتخطيط GrapQL أو الاستعلامات التي يتيحها، افتح النافذة "docs" أو النافذة "schema" في أرضية عمل GraphQL. وإن واجهتك صعوبة في استخدام المعرف <code>id</code> مثل متغير في الاستعلام، توقف قليلًا لتطلع على <a href="https://www.apollographql.com/docs/react/data/queries/" rel="external nofollow">توثيق</a> Apollo Client بما يخص الاستعلامات.
</p>

<p>
	لتطلع على طريقة التوجه إلى <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87-r1435/" rel="">عنوان URL</a> في المتصفح، اقرأ <a href="https://docs.expo.io/workflow/linking/" rel="external nofollow">توثيق Expo </a> حول واجهة الربط البرمجية Linking <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>، لأنك ستحتاج ذلك عند تنفيذك للزر الذي يفتح المستودع في GitHub.
</p>

<p>
	ينبغي أن تمتلك واجهة العرض عنوانًا خاصًا بها، ومن الجيد أن تحدد المعرّف المميز <code>id</code> للمستودع ضمن المسار الذي يوجهك نحو هذا العنوان مثل معامل مسار، كي تستطيع الوصول إليه باستخدام الخطاف <a href="https://reacttraining.com/react-router/native/api/Hooks/useparams" rel="external nofollow">useParams</a>. ينبغي أن يكون المستخدم قادرًا على الوصول إلى واجهة العرض بالضغط على المستودع ضمن قائمة المستودعات. ومن الممكن تنفيذ ذلك بتغليف المكوّن <code>RepositoryItem</code> داخل المكّون البنيوي <a href="https://reactnative.dev/docs/pressable" rel="external nofollow">Pressable</a> في المكوّن <code>RepositoryList</code>، ثم استخدام الدالة <code>navigate</code> لتغيير العنوان من خلال معالج الحدث <code>onPress</code>. استخدم الخطاف <a href="https://reactrouter.com/docs/en/v6/api#usenavigate" rel="external nofollow">useNavigate</a> أيضًا للوصول إلى الكائن دالة <code>navigate</code>.
</p>

<p>
	ستبدو النسخة النهائية لواجهة عرض مستودع واحد قريبة من الشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="105865" href="https://academy.hsoub.com/uploads/monthly_2022_08/single_repository_view_01.png.8070772deea9a357163f5fbe894f86dc.png" rel="" data-fileext="png"><img alt="single_repository_view_01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="105865" data-unique="xbiat8hjz" style="width: 420px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_08/single_repository_view_01.thumb.png.d8b0e361b6d510449caf0af8691501b3.png"></a>
</p>

<h3>
	10.20: قائمة بالآراء حول مستودع
</h3>

<p>
	بعد أن أنجزنا واجهة عرض لمستودع وحيد، سنعرض تقييمات المستخدمين ضمن هذه الواجهة. ستجد التقييمات في الحقل <code>reviews</code> العائد للنوع "Repository" في تخطيط GraphQL. تشكل التقييمات قائمةً صغيرةً مرقمةً يمكن الحصول عليها باستخدام الاستعلام <code>repositories</code>. وإليك مثالًا عن طريقة إحضار قائمة التقييمات لمستودع واحد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_52" style=""><span class="pun">{</span><span class="pln">
  repository</span><span class="pun">(</span><span class="pln">id</span><span class="pun">:</span><span class="pln"> </span><span class="str">"jaredpalmer.formik"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    id
    fullName
    reviews </span><span class="pun">{</span><span class="pln">
      edges </span><span class="pun">{</span><span class="pln">
        node </span><span class="pun">{</span><span class="pln">
          id
          text
          rating
          createdAt
          user </span><span class="pun">{</span><span class="pln">
            id
            username
          </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يحتوي الحقل <code>text</code> لكل تقييم على رأي المستخدم للنص، ويحتوي الحقل <code>rating</code> على قيمة عددية بين 0 و100، أما الحقل <code>craetedAt</code> فيحتوي على بيانات إنشاء هذا التقييم، وأخيرًا يحتوي الحقل <code>user</code> معلومات عن مُنشئ التقييم ويحمل النوع "User".
</p>

<p>
	نريد أن نعرض التقييمات على شكل شريط تمرير، مما يجعل المكوّن البنيوي <a href="https://wiki.hsoub.com/ReactNative/flatlist" rel="external">FlatList</a> ملائمًا لأداء المهمة. ولكي تعرض معلومات مستودع محدد في أعلى الواجهة، استخدم الخاصية <a href="https://wiki.hsoub.com/ReactNative/flatlist#ListHeaderComponent" rel="external">ListHeaderComponent</a> للمكون <code>FlatList</code>. يمكنك أيضًا استخدام المكوّن البنيوي <a href="https://wiki.hsoub.com/ReactNative/flatlist#ItemSeparatorComponent" rel="external">ItemSeparatorComponent</a> لإضافة مساحة بيضاء بين العناصر المعروضة كما هو الحال بين عناصر المكوّن <code>RepositoryList</code> مثلًا. إليك مثالًا عن هيكلية التنفيذ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_54" style=""><span class="kwd">const</span><span class="pln"> </span><span class="typ">RepositoryInfo</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> repository </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// Repository's information implemented in the previous exercise</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ReviewItem</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> review </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// Single review item</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">SingleRepository</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">FlatList</span><span class="pln">
      data</span><span class="pun">={</span><span class="pln">reviews</span><span class="pun">}</span><span class="pln">
      renderItem</span><span class="pun">={({</span><span class="pln"> item </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">ReviewItem</span><span class="pln"> review</span><span class="pun">={</span><span class="pln">item</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;}</span><span class="pln">
      keyExtractor</span><span class="pun">={({</span><span class="pln"> id </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> id</span><span class="pun">}</span><span class="pln">
      </span><span class="typ">ListHeaderComponent</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">RepositoryInfo</span><span class="pln"> repository</span><span class="pun">={</span><span class="pln">repository</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;}</span><span class="pln">
      </span><span class="com">// ...</span><span class="pln">
    </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">SingleRepository</span><span class="pun">;</span></pre>

<p>
	ستبدو النسخة النهائية عن تطبيق قائمة المستودعات مشابهة للشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="105864" href="https://academy.hsoub.com/uploads/monthly_2022_08/single_repository_review_show_02.png.6424eab0451357b1ac7730e19648b6e8.png" rel="" data-fileext="png"><img alt="single_repository_review_show_02.png" class="ipsImage ipsImage_thumbnailed" data-fileid="105864" data-unique="6ik4rs3t9" src="https://academy.hsoub.com/uploads/monthly_2022_08/single_repository_review_show_02.thumb.png.313293fef357493218cec58da012f73a.png"></a>
</p>

<p>
	البيانات التي تراها تحت اسم المستخدم لمنشئ التقييم هي تاريخ الإنشاء الذي نجده في الحقل <code>createdAt</code> وهو من النوع "Review"، وينبغي أن يكون تنسيق البيانات مريحًا للمستخدم مثل الصيغة "يوم.شهر.سنة". قد يساعدك تثبيت المكتبة <a href="https://date-fns.org/" rel="external nofollow">date-fns</a> على تنسيق تاريخ الإنشاء بالاستفادة من الدالة <a href="https://date-fns.org/v2.13.0/docs/format" rel="external nofollow">format</a>.
</p>

<p>
	يمكن إنجاز الحاوية ذات الشكل الدائري باستخدام خاصية التنسيق <code>borderRadius</code>. ولتفعل هذا عليك تثبيت قيمتي الخاصيتين <code>width</code> و <code>height</code>، ثم ضبط خاصية التنسيق <code>border-radius</code> عند القيمة "width/2".
</p>

<h3>
	10.21: نموذج التقييم
</h3>

<p>
	أنجز نموذجًا لإنشاء تقييم جديد لمستودع مستخدمًا المكتبة Formik. ينبغي أن يضم النموذج أربعة حقول هي:
</p>

<ul>
	<li>
		اسم دخول مالك المستودع.
	</li>
	<li>
		اسم المستودع.
	</li>
	<li>
		التقييم على هيئة قيمة عددية.
	</li>
	<li>
		رأي المستخدم على هيئة نص.
	</li>
</ul>

<p>
	تحقق من الحقول مستخدمًا تخطيط Yup، بحيث تتأكد أن:
</p>

<ul>
	<li>
		اسم المستخدم لمالك المستودع هو قيمة نصية إجبارية.
	</li>
	<li>
		اسم المستودع هو قيمة نصية إجبارية.
	</li>
	<li>
		التقييم هو قيمة عددية إجبارية بين 0 و100.
	</li>
	<li>
		رأي المستخدم هو قيمة نصية اختيارية.
	</li>
</ul>

<p>
	اطلع على توثيق Yup لإيجاد المُقيَّمات validators المناسبة، واستخدم رسائل خطأ مناسبة عند استخدامك لهذه المُقيِّمات، إذ يمكنك تعريف رسالة الخطأ مثل وسيط للتابع <code>message</code> العائد للمقيّم. يمكنك أيضًا توسيع نص الحقل ليمتد على عدة أسطر باستعمال الخاصية <a href="https://wiki.hsoub.com/ReactNative/textinput#multiline" rel="external">multiline</a> العائدة للمكون <code>TextInput</code>.
</p>

<p>
	استخدم الطفرة <code>createReview</code> لإنشاء تقييم جديد، واطلع على وسطاء الطفرة بفتح إحدى النافذتين "docs" أو "schema " في أرضية عمل GraphQL. ويمكنك استخدام الخطاف <a href="https://www.apollographql.com/docs/react/api/react-hooks/#usemutation" rel="external nofollow">useMutation</a> لإرسال الطفرة إلى خادم Apollo.
</p>

<p>
	بعد نجاح عمل الطفرة <code>createReview</code>، حوّل المستخدم إلى واجهة عرض المستودع التي أنجزناها في التمرين السابق. نفّذ ذلك باستخدام التابع <code>navigate</code> بعد أن تستخرج كائن التاريخ باستخدام الخطاف <a href="https://reactrouter.com/docs/en/v6/api#usenavigate" rel="external nofollow">useNavigate</a>. يمتلك التقييم الذي أنشأته حقلًا باسم "repositoryId" يمكنك استخدامه لتأسيس مسار لعنوان التقييم.
</p>

<p>
	استخدم <a href="https://www.apollographql.com/docs/react/data/queries/#configuring-fetch-logic" rel="external nofollow">سياسة الإحضار</a> "cache-and-network" لتمنع جلب بيانات الذاكرة المؤقتة مع الاستعلام <code>repository</code> في واجهة عرض مستودع وحيد. يمكنك استخدامها مع الخطاف على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_56" style=""><span class="pln">useQuery</span><span class="pun">(</span><span class="pln">GET_REPOSITORY</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  fetchPolicy</span><span class="pun">:</span><span class="pln"> </span><span class="str">'cache-and-network'</span><span class="pun">,</span><span class="pln">
  </span><span class="com">// خيارات أخرى</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	انتبه إلى أن التقييم سيكون فقط لمستودعات GitHub العامة الموجودة فعلًا، وأن المستخدم سيقيَّم مستودعًا محددًا مرة واحدة فقط. ليس عليك الآن التعامل مع حالات الخطأ هذه، بل فقط إظهار الخطأ مع الرمز المحدد والرسالة المناسبة. جرّب ما نفّذته على أحد المستودعات العامة التي تملكها، أو أية مستودعات عامة أخرى.
</p>

<p>
	ينبغي أن يتمكن المستخدم من الوصول إلى واجهة التقييم من خلال شريط التطبيق. لذلك أنشئ نافذة جديدة عنوانها "Create a review". ينبغي أن تكون النافذة مرئية للمستخدم الذي سجل دخوله فقط. وعليك أن تحدد أيضًا عنوانًا لواجهة التقييم.
</p>

<p>
	ستبدو النسخة النهائية للتطبيق مشابهة للشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="105863" href="https://academy.hsoub.com/uploads/monthly_2022_08/review_form_03.png.ffc8d45147200f710e00479cb7037701.png" rel="" data-fileext="png"><img alt="review_form_03.png" class="ipsImage ipsImage_thumbnailed" data-fileid="105863" data-unique="jfpdshfvy" style="width: 350px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_08/review_form_03.thumb.png.e319bd836dca8bb72c4b24b538f65607.png"></a>
</p>

<p>
	التُقطت شاشة التطبيق هذه بعد إخفاق إرسال بيانات نموذج لإظهار شكله في هذه الحالة.
</p>

<h3>
	10.22: نموذج تسجيل مستخدم جديد
</h3>

<p>
	أنجز نموذجًا لتسجيل مستخدم جديد مستخدمًا Formik. ينبغي أن يضم النموذج ثلاثة حقول: اسم مستخدم، كلمة مرور، وتأكيدًا لكلمة المرور. تحقق من صحة البيانات في النموذج مستخدمًا تخطيط Yup ومتبعًا مايلي:
</p>

<ul>
	<li>
		اسم المستخدم هو سلسلة نصية إجبارية طولها بين 1 و 30.
	</li>
	<li>
		كلمة المرور هي سلسلة نصية إجبارية طولها بين 1 و 5.
	</li>
	<li>
		تأكيد كلمة المرور يتطابق تمامًا مع كلمة المرور.
	</li>
</ul>

<p>
	قد يربكك قليلًا تأكيد كلمة المرور، لكن يمكن إنجاز الأمر بالاستفادة من التابعين <a href="https://github.com/jquense/yup#mixedoneofarrayofvalues-arrayany-message-string--function-schema-alias-equals" rel="external nofollow">oneOf</a> و<a href="https://github.com/jquense/yup#yuprefpath-string-options--contextprefix-string--ref" rel="external nofollow">ref</a> كما هو مبين في <a href="https://github.com/jaredpalmer/formik/issues/90#issuecomment-354873201" rel="external nofollow">إرشادات</a> هذا المشروع.
</p>

<p>
	يمكنك إنشاء مستخدم جديدة باستخدام الطفرة <code>createUser</code>، ويمكنك الإطلاع على كيفية استخدامها من خلال توثيق أرضية عمل Apollo.
</p>

<p>
	سجّل دخول المستخدم الجديد بعد إنشاء حسابه الخاص مستخدمًا الخطاف <code>useSignIn</code> كما فعلنا في نموذج تسجيل الدخول. وجِّه المستخدم الجديد بعد تسجيل دخوله إلى واجهة عرض المستودعات المقيّمة.
</p>

<p>
	ينبغي أن يكون المستخدم قادرًا على الوصول إلى واجهة تسجيل مستخدم جديد من خلال شريط التطبيق، وذلك بالضغط على نافذة "Sign up" التي ستظهر فقط للمستخدم قبل أن يسجّل دخوله.
</p>

<p>
	ستبدو النسخة النهائية لواجهة تسجيل مستخدم جديد مشابهة للصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="105861" href="https://academy.hsoub.com/uploads/monthly_2022_08/reposotiry_list_signup_04.png.4945da7c89e3e6f3c1868eb6b0f1d3ab.png" rel="" data-fileext="png"><img alt="reposotiry_list_signup_04.png" class="ipsImage ipsImage_thumbnailed" data-fileid="105861" data-unique="od2r6jlzb" style="width: 350px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_08/reposotiry_list_signup_04.thumb.png.4931ecaffacaac7d3bc6900d3d26e118.png"></a>
</p>

<p>
	التُقطت شاشة التطبيق هذه بعد إخفاق إرسال بيانات نموذج لإظهار شكله في هذه الحالة.
</p>

<h3>
	10.23: ترتيب بيانات تطبيق قائمة المستودعات المقّيمة
</h3>

<p>
	تُرتّب المستودعات حتى هذه اللحظة وفقًا لتاريخ تقييم المستودع. أنجز آلية تسمح للمستخدم أن يختار أساسًا لترتيب هذه المستودعات وفقًا لما يلي:
</p>

<ul>
	<li>
		المستودعات الأخيرة: سيكون المستودع الذي قُيَّم آخيرًا وللمرة الأولى في أعلى القائمة. هذا الترتيب هو المتبع حاليًا، وينبغي أن يكون الخيار الإفتراضي.
	</li>
	<li>
		المستودعات الأعلى تقييمًا: سيكون المستودع الذي يحمل أعلى قيمة لمعدّل التقييم في أعلى القائمة.
	</li>
	<li>
		المستودعات الأدنى تقييمًا: سيكون المستودع الذي يحمل أدنى قيمة لمعدّل التقييم في أعلى القائمة.
	</li>
</ul>

<p>
	يمتلك الاستعلام <code>repositories</code> الذي يحضر قائمة المستودعات وسيطًا يدعى <code>orderby</code> يمكنك استخدامه لتحديد أسلوب الترتيب المتبع، إذ يمتلك هذه الوسيط قيمتين فقط هما:
</p>

<ul>
	<li>
		<code>CREATED_AT</code>: الترتيب وفق تاريخ التقييم لأول مرة.
	</li>
	<li>
		<code>RATING_AVERAGE</code>: الترتيب على أساس معدل التقييم.
	</li>
</ul>

<p>
	كما يمتلك الاستعلام وسيطًا يدعى <code>orderDirection</code> ويستخدم لتغيير جهة الترتيب. يملك الوسيط الأخير قيمتين، هما: <code>ASC</code> (تصاعدي، من التقييم الأدنى إلى الأعلى) و <code>DESC</code> (تنازلي، من التقييم الأعلى إلى الأدنى).
</p>

<p>
	يمكن المحافظة على خيار الترتيب باستخدام الخطاف <a href="https://wiki.hsoub.com/React/hooks_reference#useState" rel="external">useState</a> على سبيل المثال، كما يمكن تمرير المتغيرات التي يستخدمها الاستعلام <code>repositories</code> إلى الخطاف <code>useRepositories</code> مثل وسطاء.
</p>

<p>
	يمكنك أيضًا استخدام المكتبة <a href="https://www.npmjs.com/package/react-native-picker-select" rel="external nofollow">react-native-picker</a> أو المكوّن <a href="https://callstack.github.io/react-native-paper/menu.html" rel="external nofollow">Menu</a> العائد للمكتبة <a href="https://callstack.github.io/react-native-paper/" rel="external nofollow">React Native Paper</a> لإنجاز شيفرة ترتيب القائمة، كما يمكنك استخدام الخاصية <a href="https://wiki.hsoub.com/ReactNative/flatlist#ListHeaderComponent" rel="external">ListHeaderComponent</a> العائدة للمكوّن <code>FlatList</code> لتزويدك بترويسة تحتوي على مكوّن الاختيار.
</p>

<p>
	ستبدو النسخة النهائية لهذه الميزة، وبناء على مكوَّن الاختيار الذي اعتمدته، قريبةً من الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="105859" href="https://academy.hsoub.com/uploads/monthly_2022_08/list_order_05.png.a0fe4679cb8e93f7045b7c5af5ce9f46.png" rel="" data-fileext="png"><img alt="list_order_05.png" class="ipsImage ipsImage_thumbnailed" data-fileid="105859" data-unique="ki0nbv5jt" src="https://academy.hsoub.com/uploads/monthly_2022_08/list_order_05.thumb.png.b22e9930c3de318ea427cb1658bade99.png"></a>
</p>

<h3>
	10.24: انتقاء مستودعات محددة من القائمة
</h3>

<p>
	يتيح خادم Apollo ترشيح المستودعات بناءً على اسمها أو اسم مالكها، ويمكن إنجاز ذلك باستخدام الوسيط <code>searchKeyword</code> العائد للاستعلام <code>repositories</code>. إليك مثالًا عن كيفية استخدام الوسيط مع الاستعلام:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_62" style=""><span class="pun">{</span><span class="pln">
  repositories</span><span class="pun">(</span><span class="pln">searchKeyword</span><span class="pun">:</span><span class="pln"> </span><span class="str">"ze"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    edges </span><span class="pun">{</span><span class="pln">
      node </span><span class="pun">{</span><span class="pln">
        id
        fullName
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أنجز ميزةً في التطبيق لترشيح قائمة المستودعات المقيّمة بناءً على كلمة محددة. ينبغي أن يكون المستخدم قادرًا على الكتابة ضمن مربع إدخال نصي وستظهر نتائج الفلترة وفقًا للكلمة المكتوبة مباشرة أثناء الكتابة. يمكنك استخدام المكوّن البسيط <code>TextInput</code>، أو استخدام مكوّن أكثر عصرية مثل المكوّن <a href="https://callstack.github.io/react-native-paper/searchbar.html" rel="external nofollow">Searchbar</a> العائد للمكتبة React Native Paper على أنه مربع إدخال نصي. ضع مكوّن الإدخال النصي ضمن ترويسة المكوّن <code>FlatList</code>.
</p>

<p>
	ولتفادي تنفيذ عدد كبير من الاستعلامات أثناء كتابة المستخدم للكلمة بسرعة، اختر آخر سلسة كتبها بعد تأخير بسيط. تدعى هذه التقنية بالتريّث <a href="https://lodash.com/docs/4.17.15#debounce" rel="external nofollow">debouncing</a>. تمثّل المكتبة <a href="https://www.npmjs.com/package/use-debounce" rel="external nofollow">use-debounce</a> حلًا جيدًا لتأخير متغيّر الحالة، بعد أن تستخدم زمن انتظار معقول مثل 500 ميلي ثانية. خزّن قيمة النص الذي يُكتب في مربع الإدخال مستخدمًا الخطاف <code>useState</code>، ومرر القيمة المُؤخَّرة إلى الاستعلام كقيمة للوسيط <code>searchKeyword</code>.
</p>

<p>
	قد تعترضك مشكلة فقدان مكون الإدخال النصي تركيز الدخل بعد كتابة كل حرف، وذلك لأن المحتوى الذي تقدّمه الخاصية <code>ListHeaderComponent</code> سيُلغى باستمرار. يمكن حل المشكلة بتحويل المكوّن المسؤول عن تصيير المكوّن <code>FlatList</code> إلى مكوّن صنف class component ومن ثم تعريف دالة تصيير الترويسة إلى خاصية للصنف على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_64" style=""><span class="kwd">export</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">RepositoryListContainer</span><span class="pln"> extends </span><span class="typ">React</span><span class="pun">.</span><span class="typ">Component</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  renderHeader </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// يحوي خاصيات المكون this.props</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> props </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">props</span><span class="pun">;</span><span class="pln">

    </span><span class="com">// ...</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">RepositoryListHeader</span><span class="pln">
      </span><span class="com">// ...</span><span class="pln">
      </span><span class="pun">/&gt;</span><span class="pln">
    </span><span class="pun">);</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

  render</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">FlatList</span><span class="pln">
        </span><span class="com">// ...</span><span class="pln">
        </span><span class="typ">ListHeaderComponent</span><span class="pun">={</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">renderHeader</span><span class="pun">}</span><span class="pln">
      </span><span class="pun">/&gt;</span><span class="pln">
    </span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستبدو النسخة النهائية لميزة انتقاء المستودعات قريبةً من الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="105858" href="https://academy.hsoub.com/uploads/monthly_2022_08/list_filitering_06.png.1a12ff28fe629db5f1c9ec4c1fdb3691.png" rel="" data-fileext="png"><img alt="list_filitering_06.png" class="ipsImage ipsImage_thumbnailed" data-fileid="105858" data-unique="05rr3x4i1" src="https://academy.hsoub.com/uploads/monthly_2022_08/list_filitering_06.thumb.png.97c8efaf2b81e03ded82688529ebd933.png"></a>
</p>

<h2>
	الترقيم بطريقة المؤشرات
</h2>

<p>
	عندما تعيد الواجهة البرمجية قائمةً مرتبةً من العناصر من مجموعةٍ ما، فإنها ستعيد مجموعةً جزئيةً من العناصر من أصل المجموعة الكاملة لتقليل عرض حزمة الاتصال مع <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">الخادم</a> وتقليل الذاكرة المستخدمة لتخزين القائمة في تطبيق المستخدم. يمكن أن نطلب تلك المجموعة الجزئية لتوافق معاملات محددة لكي يستطيع المستخدم أن يطلب مثلًا أول عشرين عنصرًا منها ابتداءًا من عنصر محدد. تدعى هذه التقنية عادة بالترقيم pagination. وعندما نستطيع الحصول على قائمة بعد عنصر محدد بمؤشر معيّن، سنكون أمام الترقيم المبني على المؤشرات cursor-based pagination.
</p>

<p>
	فالمؤشر إذًا هو تحديدٌ لعنصر في قائمة مرتبة. لنلقي نظرةً على قائمة المستودعات المرقمة التي يعيدها الاستعلام <code>repositories</code> باستخدام الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_67" style=""><span class="pun">{</span><span class="pln">
  repositories</span><span class="pun">(</span><span class="pln">first</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    totalCount
    edges </span><span class="pun">{</span><span class="pln">
      node </span><span class="pun">{</span><span class="pln">
        id
        fullName
        createdAt
      </span><span class="pun">}</span><span class="pln">
      cursor
    </span><span class="pun">}</span><span class="pln">
    pageInfo </span><span class="pun">{</span><span class="pln">
      endCursor
      startCursor
      hasNextPage
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يبلِّغ الوسيط <code>first</code> الواجهة البرمجية ان تعيد فقط أول مستودعين. وإليك مثالًا عن الاستجابة لهذا الاستعلام:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_69" style=""><span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"repositories"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"totalCount"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"edges"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
          </span><span class="str">"node"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="str">"id"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"zeit.next.js"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"fullName"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"zeit/next.js"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"createdAt"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"2020-05-15T11:59:57.557Z"</span><span class="pln">
          </span><span class="pun">},</span><span class="pln">
          </span><span class="str">"cursor"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"WyJ6ZWl0Lm5leHQuanMiLDE1ODk1NDM5OTc1NTdd"</span><span class="pln">
        </span><span class="pun">},</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">
          </span><span class="str">"node"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="str">"id"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"zeit.swr"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"fullName"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"zeit/swr"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"createdAt"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"2020-05-15T11:58:53.867Z"</span><span class="pln">
          </span><span class="pun">},</span><span class="pln">
          </span><span class="str">"cursor"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"WyJ6ZWl0LnN3ciIsMTU4OTU0MzkzMzg2N10="</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">],</span><span class="pln">
      </span><span class="str">"pageInfo"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"endCursor"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"WyJ6ZWl0LnN3ciIsMTU4OTU0MzkzMzg2N10="</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"startCursor"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"WyJ6ZWl0Lm5leHQuanMiLDE1ODk1NDM5OTc1NTdd"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"hasNextPage"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يعتمد تنسيق الكائن الناتج والوسطاء على <a href="https://relay.dev/graphql/connections.htm" rel="external nofollow">مواصفات اتصالات مؤشر أرضية عمل GraphQL للترحيل</a>، والتي أصبحت مواصفات ترقيم صفحات شائعة جدًا واعتُمدت على نطاقٍ واسع في واجهة برمجة تطبيقات <a href="https://docs.github.com/en/graphql" rel="external nofollow">GitHub's GraphQL <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a>. سيحتوي الكائن الذي يحمل النتيجة مصفوفةً باسم <code>edges</code> تحتوي بدورها على عناصر لها سمتين الأولى هي العقدة node والأخرى هي المؤشر cursor؛ إذ تحتوي العقدة على المستودع بحد ذاته كما نعرف؛ أما المؤشر فهو تمثيلٌ مشفّر للعقدة بأسلوب "Base64". ويحتوي المؤشر على المعرّف id للمستودع بالإضافة إلى تاريخ إنشائه. هذه هي كل المعلومات التي نريدها لكي نشير إلى العنصر عندما تُرتَّب العناصر وفق تاريخ إنشاء المستودع. يحتوي الكائن <code>pageInfo</code>على معلومات مثل المؤشرات على بداية ونهاية المصفوفة.
</p>

<p>
	دعونا نتأمل الحالة التي نريد فيها مجموعة العناصر التي تلي آخر عنصر من المجموعة الحالية وهو العنصر "zeit/swr". يمكننا إسناد قيمة الحقل <code>endCursor</code> إلى الوسيط <code>after</code> للاستعلام على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_71" style=""><span class="pun">{</span><span class="pln">
  repositories</span><span class="pun">(</span><span class="pln">first</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> after</span><span class="pun">:</span><span class="pln"> </span><span class="str">"WyJ6ZWl0LnN3ciIsMTU4OTU0MzkzMzg2N10="</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    totalCount
    edges </span><span class="pun">{</span><span class="pln">
      node </span><span class="pun">{</span><span class="pln">
        id
        fullName
        createdAt
      </span><span class="pun">}</span><span class="pln">
      cursor
    </span><span class="pun">}</span><span class="pln">
    pageInfo </span><span class="pun">{</span><span class="pln">
      endCursor
      startCursor
      hasNextPage
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	والآن سنحصل على العنصرين التاليين، وسنستمر في هذه العملية حتى يأخذ الحقل <code>hasNextPage</code> القيمة <code>false</code>، وهذا يعني أننا وصلنا آخر القائمة، ولتتعمق أكثر في الترقيم المتعلق بالمؤشرات، إقرأ المقالة التي عنوانها <a href="https://engineering.shopify.com/blogs/engineering/pagination-relative-cursors" rel="external nofollow">Pagination with Relative Cursors</a> على موقع Shopify، حيث تقدم المقالة الكثير من التفاصيل عن طريقة كتابة الشيفرة بالإضافة إلى الفوائد من استخدام الترقيم بالمؤشرات مقارنةً بالترقيم وفق ترتيب العنصر index-based.
</p>

<h2>
	التمرير اللانهائي
</h2>

<p>
	تُصمّم القوائم المرتبة عموديًا في <a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%B3%D8%B7%D8%AD-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8/" rel="">تطبيقات سطح المكتب</a> أو تطبيقات الهواتف الذكية بتقنية تدعى التمرير اللانهائي - infinite scrolling. ويعتمد مبدأ هذه التقنية على النقاط التالية:
</p>

<ul>
	<li>
		إحضار المجموعة الأولية من العناصر.
	</li>
	<li>
		عند وصول المستخدم إلى آخر عنصر من هذه المجموعة، يجري إحضار المجموعة التالية من العناصر التي تبدأ من العنصر الذي يلي آخر عنصر من المجموعة السابقة.
	</li>
</ul>

<p>
	تتكرر الخطوة الثانية حتى يتوقف المستخدم عن تمرير شريط القائمة أو عندما يتجاوز حدًا معينًا من مرات التمرير. ويشير الاسم "تمرير لانهائي" إلى أن القائمة ستبدو لانهائية، فسيبقى المستخدم قادرًا على تمرير العناصر ضمن الشريط وستُعرض العناصر بصورةٍ مستمرة أيضًا.
</p>

<p>
	لنرى كيف سننفذ ذلك عمليًا من خلال خطاف Apollo Client الذي يُدعى <code>useQuery</code>. يضم <a href="https://www.apollographql.com/docs/react/data/pagination/#cursor-based" rel="external nofollow">توثيق</a> Apollo Client تفاصيل كثيرة عن طريقة تنفيذ الترقيم باستخدام المؤشرات. لننجز إذًا ميزة التمرير اللانهائي لعرض قائمة المستودعات المقيّمة.
</p>

<p>
	علينا أولًا أن نحدد متى يصل المستخدم إلى آخر عنصر في القائمة. ولحسن الحظ، يمتلك المكون <code>FlatList</code> الخاصية <code>onEndReached</code> التي تستدعى الدالة التي ستنفذ العملية عندما يصل المستخدم إلى آخر عنصر من القائمة. يمكنك التحكم بوقت استدعاء الدالة <code>onEndReach</code> باستخدام الخاصية <a href="https://wiki.hsoub.com/ReactNative/flatlist#onEndReachedThreshold" rel="external">onEndReachedThreshold</a>. غيِّر شيفرة المكوّن <code>FlatList</code> الموجود ضمن المكوّن <code>RepositoryList</code> لكي يستدعي الدالة حالما يصل المستخدم إلى العنصر الأخير:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_73" style=""><span class="kwd">export</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="typ">RepositoryListContainer</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln">
  repositories</span><span class="pun">,</span><span class="pln">
  onEndReach</span><span class="pun">,</span><span class="pln">
  </span><span class="com">/* ... */</span><span class="pun">,</span><span class="pln">
</span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> repositoryNodes </span><span class="pun">=</span><span class="pln"> repositories
    </span><span class="pun">?</span><span class="pln"> repositories</span><span class="pun">.</span><span class="pln">edges</span><span class="pun">.</span><span class="pln">map</span><span class="pun">((</span><span class="pln">edge</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> edge</span><span class="pun">.</span><span class="pln">node</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">:</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">FlatList</span><span class="pln">
      data</span><span class="pun">={</span><span class="pln">repositoryNodes</span><span class="pun">}</span><span class="pln">
      </span><span class="com">// ...</span><span class="pln">
      onEndReached</span><span class="pun">={</span><span class="pln">onEndReach</span><span class="pun">}</span><span class="pln">
      onEndReachedThreshold</span><span class="pun">={</span><span class="lit">0.5</span><span class="pun">}</span><span class="pln">
    </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> repositories </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useRepositories</span><span class="pun">(</span><span class="com">/* ... */</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> onEndReach </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'You have reached the end of the list'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">RepositoryListContainer</span><span class="pln">
      repositories</span><span class="pun">={</span><span class="pln">repositories</span><span class="pun">}</span><span class="pln">
      onEndReach</span><span class="pun">={</span><span class="pln">onEndReach</span><span class="pun">}</span><span class="pln">
      </span><span class="com">// ...</span><span class="pln">
    </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pun">;</span></pre>

<p>
	جرّب عملية التمرير إلى نهاية قائمة المستودعات، وستظهر رسالةً في سجل العمل مفادها أنك وصلت إلى العنصر الأخير.
</p>

<p>
	علينا الآن أن نحضر مزيدًا من المستودعات في اللحظة التي نصل فيها إلى نهاية العناصر. يمكن إنجاز ذلك باستخدام الدالة <a href="https://www.apollographql.com/docs/react/data/pagination/#cursor-based" rel="external nofollow">fetchMore</a> التي يؤمنها الخطاف <code>useQuery</code>. يمكننا استخدام <a href="https://www.apollographql.com/docs/react/caching/cache-field-behavior/" rel="external nofollow">سياسة الحقل field policy</a> لوصف عميل Apollo وكيفية دمج المستودعات الموجودة في ذاكرة التخزين المؤقت مع المجموعة التالية من المستودعات، إذ تُستخدم سياسات الحقول عمومًا لتخصيص سلوك ذاكرة التخزين المؤقت أثناء عمليات القراءة والكتابة باستخدام دوال <a href="https://www.apollographql.com/docs/react/caching/cache-field-behavior/#the-read-function" rel="external nofollow">القراءة</a> و<a href="https://www.apollographql.com/docs/react/caching/cache-field-behavior/#the-merge-function" rel="external nofollow">الدمج</a>.
</p>

<p>
	دعنا نضيف سياسة حقل للاستعلام <code>repositories</code> في ملف "apolloClient.js" على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_75" style=""><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ApolloClient</span><span class="pun">,</span><span class="pln"> </span><span class="typ">InMemoryCache</span><span class="pun">,</span><span class="pln"> createHttpLink </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> setContext </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client/link/context'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Constants</span><span class="pln"> from </span><span class="str">'expo-constants'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> relayStylePagination </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client/utilities'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> apolloUri </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Constants</span><span class="pun">.</span><span class="pln">manifest</span><span class="pun">.</span><span class="pln">extra</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> httpLink </span><span class="pun">=</span><span class="pln"> createHttpLink</span><span class="pun">({</span><span class="pln">
  uri</span><span class="pun">:</span><span class="pln"> apolloUri</span><span class="pun">,</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> cache </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">InMemoryCache</span><span class="pun">({</span><span class="pln">
  typePolicies</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      fields</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        repositories</span><span class="pun">:</span><span class="pln"> relayStylePagination</span><span class="pun">(),</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> createApolloClient </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">authStorage</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> authLink </span><span class="pun">=</span><span class="pln"> setContext</span><span class="pun">(</span><span class="pln">async </span><span class="pun">(</span><span class="pln">_</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> headers </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> accessToken </span><span class="pun">=</span><span class="pln"> await authStorage</span><span class="pun">.</span><span class="pln">getAccessToken</span><span class="pun">();</span><span class="pln">

      </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="pun">...</span><span class="pln">headers</span><span class="pun">,</span><span class="pln">
          authorization</span><span class="pun">:</span><span class="pln"> accessToken </span><span class="pun">?</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Bearer</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">accessToken</span><span class="pun">}`</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">};</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">e</span><span class="pun">);</span><span class="pln">

      </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        headers</span><span class="pun">,</span><span class="pln">
      </span><span class="pun">};</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ApolloClient</span><span class="pun">({</span><span class="pln">
    link</span><span class="pun">:</span><span class="pln"> authLink</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">httpLink</span><span class="pun">),</span><span class="pln">
    cache</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> createApolloClient</span><span class="pun">;</span></pre>

<p>
	كما ذكرنا سابقًا، يعتمد تنسيق كائن نتيجة ترقيم الصفحات والوسطاء على مواصفات ترقيم الصفحات الخاصة بالترحيل Relay، ولحسن الحظ، يوفر Apollo Client سياسة حقل محددة مسبقًا، <code>relayStylePagination</code>، والتي يمكن استخدامها في هذه الحالة.
</p>

<p>
	غيّر في شيفرة الخطاف <code>useRepositories</code> بحيث يعيد الدالة <code>fetchMore</code> بعد فَكِّ تشفيرها، بحيث تستدعي الدالة <code>fetchMore</code> الفعلية مع الحقل <code>endCursor</code>، ثم يُحدّث بعدها الاستعلام بصورة صحيحة وفقًا للبيانات المُحضرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_77" style=""><span class="kwd">const</span><span class="pln"> useRepositories </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">variables</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> data</span><span class="pun">,</span><span class="pln"> loading</span><span class="pun">,</span><span class="pln"> fetchMore</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln">result </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useQuery</span><span class="pun">(</span><span class="pln">GET_REPOSITORIES</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    variables</span><span class="pun">,</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> handleFetchMore </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> canFetchMore </span><span class="pun">=</span><span class="pln"> </span><span class="pun">!</span><span class="pln">loading </span><span class="pun">&amp;&amp;</span><span class="pln"> data</span><span class="pun">?.</span><span class="pln">repositories</span><span class="pun">.</span><span class="pln">pageInfo</span><span class="pun">.</span><span class="pln">hasNextPage</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">canFetchMore</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    fetchMore</span><span class="pun">({</span><span class="pln">
      variables</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        after</span><span class="pun">:</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">repositories</span><span class="pun">.</span><span class="pln">pageInfo</span><span class="pun">.</span><span class="pln">endCursor</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">...</span><span class="pln">variables</span><span class="pun">,</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    repositories</span><span class="pun">:</span><span class="pln"> data</span><span class="pun">?.</span><span class="pln">repositories</span><span class="pun">,</span><span class="pln">
    fetchMore</span><span class="pun">:</span><span class="pln"> handleFetchMore</span><span class="pun">,</span><span class="pln">
    loading</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">...</span><span class="pln">result</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	تأكد من وجود الحقلين <code>pageInfo</code> و <code>cursor</code> في الاستعلام <code>repositories</code> كما هو موضح في مثال الترقيم. كما ينبغي عليك أن تضيف الوسيطين <code>first</code> و <code>after</code> إلى الاستعلام.
</p>

<p>
	ستستدعي الدالة <code>handleFetchMore</code> الدالة <code>fetchMore</code> العائدة إلى Apollo client إن كان هناك المزيد من العناصر التي ينبغي إحضارها، وذلك بناء على قيمة الخاصية <code>hasNextPage</code>، ومن المفترض أن نمنع إحضار عناصر جديدة طالما أن عملية الإحضار سابقةً لا تزال قيد التنفيذ، إذ ستكون قيمة الحقل <code>loading</code> في هذه الحالة <code>true</code>. سنزود الاستعلام في الدالة <code>fetchMore</code> بالمتغير <code>after</code> الذي يتلقى آخر قيمة <code>endCursor</code> للحقل.
</p>

<p>
	وتكون الخطوة الأخيرة استدعاء الدالة <code>fetchMore</code> ضمن دالة معالجة الحدث <code>onEndReach</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_79" style=""><span class="kwd">const</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> repositories</span><span class="pun">,</span><span class="pln"> fetchMore </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useRepositories</span><span class="pun">({</span><span class="pln">
    first</span><span class="pun">:</span><span class="pln"> </span><span class="lit">8</span><span class="pun">,</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> onEndReach </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fetchMore</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">RepositoryListContainer</span><span class="pln">
      repositories</span><span class="pun">={</span><span class="pln">repositories</span><span class="pun">}</span><span class="pln">
      onEndReach</span><span class="pun">={</span><span class="pln">onEndReach</span><span class="pun">}</span><span class="pln">
      </span><span class="com">// ...</span><span class="pln">
    </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pun">;</span></pre>

<p>
	استخدم قيمة صغيرة نسبيًا (8 مثًلا) للوسيط <code>first</code> عندما تجرب التمرير اللانهائي، وهكذا لن تضطر إلى عرض كثيرٍ من المستودعات، وقد تواجهك أيضًا مشكلة الاستدعاء المباشر لمعالج الحدث <code>onEndReach</code> بعد تحميل واجهة العرض، والسبب على الأرجح، هو العدد الكبير للمستودعات في القائمة، وبالتالي ستصل إلى نهاية القائمة مباشرةً. يمكنك الالتفاف على هذه المشكلة بزيادة قيمة الوسيط <code>first</code>. وبمجرد أن ترى أن قائمة التمرير اللانهائي تعمل جيدًا، يمكنك تجريب قيم كبيرة للوسيط <code>first</code>.
</p>

<h2>
	التمرينات 10.25- 10.27
</h2>

<h3>
	10.15: شريط تمرير لانهائي لقائمة المستودعات المقيّمة
</h3>

<p>
	نفذ شريط تمرير لانهائي لقائمة المستودعات المقيّمة. يمتلك الحقل <code>reviews</code> العائد للنوع "Repository" الوسيطين<code>after</code> و <code>first</code> كما في الاستعلام <code>repositories</code>. ويمتلك النوع "ReviewConnection" الحقل <code>pageInfo</code> كما يمتلكه النوع "RepositoryConnection".
</p>

<p>
	إليك مثالًا عن استعلام:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_82" style=""><span class="pun">{</span><span class="pln">
  repository</span><span class="pun">(</span><span class="pln">id</span><span class="pun">:</span><span class="pln"> </span><span class="str">"jaredpalmer.formik"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    id
    fullName
    reviews</span><span class="pun">(</span><span class="pln">first</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> after</span><span class="pun">:</span><span class="pln"> </span><span class="str">"WyIxYjEwZTRkOC01N2VlLTRkMDAtODg4Ni1lNGEwNDlkN2ZmOGYuamFyZWRwYWxtZXIuZm9ybWlrIiwxNTg4NjU2NzUwMDgwXQ=="</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      totalCount
      edges </span><span class="pun">{</span><span class="pln">
        node </span><span class="pun">{</span><span class="pln">
          id
          text
          rating
          createdAt
          repositoryId
          user </span><span class="pun">{</span><span class="pln">
            id
            username
          </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
        cursor
      </span><span class="pun">}</span><span class="pln">
      pageInfo </span><span class="pun">{</span><span class="pln">
        endCursor
        startCursor
        hasNextPage
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكن أن تتشابه سياسة حقل ذاكرة التخزين المؤقت مع استعلام <code>repositories</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_84" style=""><span class="kwd">const</span><span class="pln"> cache </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">InMemoryCache</span><span class="pun">({</span><span class="pln">
  typePolicies</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      fields</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        repositories</span><span class="pun">:</span><span class="pln"> relayStylePagination</span><span class="pun">(),</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">

   </span><span class="typ">Repository</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     fields</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       reviews</span><span class="pun">:</span><span class="pln"> relayStylePagination</span><span class="pun">(),</span><span class="pln">
     </span><span class="pun">},</span><span class="pln">
   </span><span class="pun">},</span><span class="pln">
 </span><span class="pun">},</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	استخدم قيمةً صغيرةً نسبيًا للوسيط <code>first</code> عندما تحاول إنجاز قائمة التمرير اللانهائي، وربما ستضطر إلى إنشاء عدة مستخدمين جدد لكي تُقيّم عددًا من المستودعات، وبذلك تصبح قائمة المستودعات المقيّمة طويلة بما يكفي لتجرّب شريط التمرير. اجعل قيمة الوسيط كبيرةً بما يكفي لعدم استدعاء معالج الحدث <code>onEndReach</code> مباشرةً عند عرض الواجهة، وصغيرةً في نفس الوقت بحيث يعرض الشريط المستودعات من جديد عند الوصول إلى نهاية القائمة. وبمجرد أن ترى أن قائمة التمرير اللانهائي تعمل جيدًا، يمكنك تجريب قيم كبيرة للوسيط <code>first</code>.
</p>

<h3>
	10.26: واجهة عرض لتقييمات المستخدم
</h3>

<p>
	أنجز واجهة تُمكِّن المستخدم من عرض ما قيّمه. ينبغي أن يكون المستخدم قادرًا على الوصول إلى هذه الواجهة من خلال الضغط على النافذة "My reviews" في شريط التطبيق بمجرد أن يسّجل دخوله. يمكنك تطبيق أسلوب التمرير اللانهائي إن أردت في هذا التمرين. وإليك الشكل الذي يمكن أن تظهر عليه هذه الواجهة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="105860" href="https://academy.hsoub.com/uploads/monthly_2022_08/my_reviews_view_07.png.8018b9c0951187e5e9cedcc464a87918.png" rel="" data-fileext="png"><img alt="my_reviews_view_07.png" class="ipsImage ipsImage_thumbnailed" data-fileid="105860" data-unique="l9uxhfdpr" style="width: 350px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_08/my_reviews_view_07.thumb.png.d678146f311bb4cf16707d1f925b6527.png"></a>
</p>

<p>
	تذكر أنه بإمكانك الحصول على المستخدم الذي سجّل دخول من خادم Apollo مستعينًا بالاستعلام <code>me</code>. سيعيد الاستعلام النوع "User" الذي يمتلك الحقل <code>review</code>. إن كنت قد أنجزت مسبقًا الاستعلام <code>me</code> يمكنك إعادة استخدامه بعد جعله قادرًا على إحضار الحقل <code>review</code> شرطيWh، وذلك بالاستفادة من توجيه GraphQL الذي يُدعى <a href="https://graphql.org/learn/queries/#directives" rel="external nofollow">include</a>.
</p>

<p>
	لنفترض أن الاستعلام الحالي قد أنجز بصورةٍ قريبة من التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_87" style=""><span class="kwd">const</span><span class="pln"> GET_CURRENT_USER </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  query </span><span class="pun">{</span><span class="pln">
    me </span><span class="pun">{</span><span class="pln">
      </span><span class="pun">#</span><span class="pln"> user fields</span><span class="pun">...</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">`;</span></pre>

<p>
	يمكنك تزويد الاستعلام بالوسيط <code>includeReview</code>واستخدام ذلك مع التوجيه <code>include</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_89" style=""><span class="kwd">const</span><span class="pln"> GET_CURRENT_USER </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  query getCurrentUser</span><span class="pun">(</span><span class="pln">$includeReviews</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Boolean</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    me </span><span class="pun">{</span><span class="pln">
      </span><span class="pun">#</span><span class="pln"> user fields</span><span class="pun">...</span><span class="pln">
      reviews </span><span class="lit">@include</span><span class="pun">(</span><span class="kwd">if</span><span class="pun">:</span><span class="pln"> $includeReviews</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        edges </span><span class="pun">{</span><span class="pln">
          node </span><span class="pun">{</span><span class="pln">
            </span><span class="pun">#</span><span class="pln"> review fields</span><span class="pun">...</span><span class="pln">
          </span><span class="pun">}</span><span class="pln">
          cursor
        </span><span class="pun">}</span><span class="pln">
        pageInfo </span><span class="pun">{</span><span class="pln">
          </span><span class="pun">#</span><span class="pln"> page info fields</span><span class="pun">...</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">`;</span></pre>

<p>
	يمتلك الوسيط <code>includeReview</code> القيمة الافتراضية <code>false</code>، لأننا لا نريد أن نسبب حملًا زائدًا على الخادم ما لم نرد صراحةً إحضار المستودعات التي قيّمها المستخدم. إن مبدأ هذا التوجيه بسيط على النحو التالي: إن كانت قيم الوسيط <code>if</code> هي <code>true</code>، أحضر الحقل، وإلا احذفه.
</p>

<h3>
	10.27: إضافة أفعال إلى واجهة عرض التقييمات
</h3>

<p>
	بعد أن أصبح المستخدم قادرًا على استعراض ما قيّمه، لنضف إلى الواجهة بعض الأفعال. ضع أسفل كل تقيم زرين، أحدهما لعرض المستودع، بحيث ينتقل المستخدم بالضغط عليه إلى واجهة عرض مستودع واحد، والآخر لحذف تقييم هذا المستودع. إليك ما قد يبدو عليه الوضع بعد إضافة الزرين:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="105862" href="https://academy.hsoub.com/uploads/monthly_2022_08/reviews_tow_buttons_08.png.fdc3cea8a7895028393468398b53075b.png" rel="" data-fileext="png"><img alt="reviews_tow_buttons_08.png" class="ipsImage ipsImage_thumbnailed" data-fileid="105862" data-unique="5r0j7pbug" src="https://academy.hsoub.com/uploads/monthly_2022_08/reviews_tow_buttons_08.thumb.png.9cd597c4ccbec2b8480da652bf10d074.png"></a>
</p>

<p>
	ينبغي أن يلي الضغط على زر الحذف رسالة تأكيد للحذف، فإن أكد المستخدم أمر الحذف ستُنفّذ العملية، وإلا سيُهمل الأمر. يمكنك إنجاز ذلك باستخدام الوحدة البرمجية <a href="https://wiki.hsoub.com/ReactNative/alert" rel="external">Alert</a>. وانتبه إلى أن استدعاء التابع <code>Alert.alert</code> لن يفتح رسالة التأكيد في واجهة عرض المنصة Expo، بل عليك استخدام تطبيق جوّال Expo أو المقلّد لترى كيف ستبدو هذه الرسالة.
</p>

<p>
	إليك الرسالة التي ينبغي أن تظهر عند الضغط على زر الحذف:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="105857" href="https://academy.hsoub.com/uploads/monthly_2022_08/delete_confirm_message_09.png.20f03271b3064e460e27bec67739997e.png" rel="" data-fileext="png"><img alt="delete_confirm_message_09.png" class="ipsImage ipsImage_thumbnailed" data-fileid="105857" data-unique="6gte4n91s" style="width: 350px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_08/delete_confirm_message_09.thumb.png.bd48a0e50fe97005bce50f9e331e75d7.png"></a>
</p>

<p>
	يمكنك حذف التقييم باستخدام الطفرة <code>deleteReview</code>. تمتلك هذه الطفرة وسيطًا واحدًا وهو المعرّف <code>id</code> للتقييم الذي سيُحذف. ومن السهل بعد تنفيذ الطفرة تحديث استعلام قائمة المستودعات باستدعاء الدالة <a href="https://www.apollographql.com/docs/react/data/queries/#refetching" rel="external nofollow">refetch</a>.
</p>

<p>
	هكذا نكون قد وصلنا إلى التمرين الأخير في هذا المقال، وقد حان وقت تسليم الإجابات إلى GitHub والإشارة إلى التمارين التي أكملتها في <a href="https://studies.cs.helsinki.fi/stats/courses/fs-react-native-2020" rel="external nofollow">منظومة تسيم التمارين</a>. انتبه إلى وضع تمارين هذا المقال في <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%87%D9%8A%D9%83%D9%84-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%AE%D9%84%D9%81%D9%8A%D8%A9-%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-unit-tests-r1132/" rel="">القسم 4</a> من منظومة التسليم.
</p>

<h2>
	مصادر إضافية
</h2>

<p>
	طالما أننا وصلنا إلى نهاية هذا القسم، دعونا نلقي نظرةً على بعض المصادر الهامة للتطوير باستخدام React Native. سنبدأ من <a href="https://wiki.hsoub.com/ReactNative" rel="external">توثيق React Native باللغة العربية</a> الذي تقدمه أكاديمية حسوب، والتي تقدم أيضًا <a href="https://wiki.hsoub.com/React" rel="external">توثيق React باللغة العربية</a>، بالإضافة إلى القسم المتخصص <a href="https://academy.hsoub.com/programming/javascript/react/" rel="">باللغة React</a> على موقع الأكاديمية، كما نشير إلى الموقع <a href="https://github.com/jondot/awesome-react-native" rel="external nofollow">Awesome React Native</a> الذي يقدم قائمةً مهمةً جدًا من المصادر مثل المكتبات والدورات التعليمية. وبما أن القائمة طويلة جدًا، لنطلع على بعض النقاط.
</p>

<h3>
	المكتبة React Native Paper
</h3>

<p>
	المكتبة Paper هي مجموعةٌ من المكوّنات المخصصة والجاهزة للاستخدام في React Native، مبنيةٌ على معايير تصميم Material التي وضعتها غوغل Google.
</p>

<p>
	صُممت <a href="https://callstack.github.io/react-native-paper/" rel="external nofollow">React Native Paper</a> من أجل لتقدم وظيفة تماثل وظائف المكتبة <a href="https://material-ui.com/" rel="external nofollow">Material-UI</a>، إذ تقدم مجموعةً واسعةً من مكوّنات واجهة المستخدم UI عالية الجودة التي تدعم إنشاء <a href="https://callstack.github.io/react-native-paper/theming.html" rel="external nofollow">سمات مخصصة</a> للتطبيق. وتتميز إعدادات استخدام React Native Paper مع تطبيقات React Native المبنية على منصة Expo بالسهولة، حيث يمكنك تجربتها مباشرة في التمارين القادمة إن أردت.
</p>

<h3>
	المكتبة Styled-components
</h3>

<p>
	سيعطيك استخدام القوالب المعرّفة المجرّدة tagged template literal -وهي إضافةٌ جديدة في JavaScript-، مع المكتبة styled-components إمكانية <a href="https://academy.hsoub.com/programming/css/%D8%AA%D9%82%D9%86%D9%8A%D8%A7%D8%AA-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-css-%D8%A7%D8%AD%D8%AA%D8%B1%D8%A7%D9%81%D9%8A%D8%A9-%D9%88%D8%B3%D9%87%D9%84%D8%A9-%D8%A7%D9%84%D8%B5%D9%91%D9%8A%D8%A7%D9%86%D8%A9-r428/" rel="">كتابة شيفرة CSS</a> فعلية لتنسيق المكوّنات، كما يلغي استخدام هذه المكتبة الحاجة إلى الربط بين المكوّنات والتنسيق، وسيكون استخدام المكوّنات مثل وحدات بناءً تنسيقات على المستوى البرمجي المنخفض سهلًا جدًا.
</p>

<p>
	تستخدم المكتبة <a href="https://styled-components.com/" rel="external nofollow">Styled-components</a> لتنسيق مكوّنات React Native باستخدام تقنية <a href="https://en.wikipedia.org/wiki/CSS-in-JS" rel="external nofollow">CSS-in-JS</a>. لقد اعتدنا على تعريف تنسيق المكوّنات في React Native باستخدام كائنات JavaScript، وبالتالي لن يكون استخدام CSS-in-JS غريبًا. لكن مقاربة المكتبة Styled-components ستختلف تمامًا عن استخدام التابع <code>StyleSheet.create</code>و الخاصية <code>style</code>.
</p>

<p>
	يُعرّف تسيق المكوّن في Styled-components باستخدام ميزة تُدعى القوالب المعرّفة المجرّدة <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates" rel="external nofollow">tagged template literal</a> أو <a href="https://academy.hsoub.com/programming/javascript/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-objects-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r645/" rel="">كائنات جافا سكربت JavaScript</a> الصِّرفة، التي تُمكننا من تعريف خصائص تنسيق جديدة للمكونات بناءً على خصائص تلك المكوّنات في زمن التنفيذ. سيتيح ذلك الكثير من الإمكانات مثل الانتقال بين السمات القاتمة والمضيئة، كما أنها <a href="https://styled-components.com/docs/advanced#theming" rel="external nofollow">تدعم كليًا موضوع السمات</a>. إليك مثالًا عن إنشاء المكوّن <code>Text</code> مع إمكانية تغيير تنسيقه بناءً على خصائصه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9067_98" style=""><span class="kwd">import</span><span class="pln"> styled from </span><span class="str">'styled-components/native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> css </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'styled-components'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">FancyText</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> styled</span><span class="pun">.</span><span class="typ">Text</span><span class="pun">`</span><span class="pln">
  color</span><span class="pun">:</span><span class="pln"> grey</span><span class="pun">;</span><span class="pln">
  font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">14px</span><span class="pun">;</span><span class="pln">

  $</span><span class="pun">{({</span><span class="pln"> isBlue </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln">
    isBlue </span><span class="pun">&amp;&amp;</span><span class="pln">
    css</span><span class="pun">`</span><span class="pln">
      color</span><span class="pun">:</span><span class="pln"> blue</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">`}</span><span class="pln">

  $</span><span class="pun">{({</span><span class="pln"> isBig </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln">
    isBig </span><span class="pun">&amp;&amp;</span><span class="pln">
    css</span><span class="pun">`</span><span class="pln">
      font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">24px</span><span class="pun">;</span><span class="pln">
      font</span><span class="pun">-</span><span class="pln">weight</span><span class="pun">:</span><span class="pln"> </span><span class="lit">700</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">`}</span><span class="pln">
</span><span class="pun">`;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Main</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">FancyText</span><span class="pun">&gt;</span><span class="typ">Simple</span><span class="pln"> text</span><span class="pun">&lt;/</span><span class="typ">FancyText</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">FancyText</span><span class="pln"> isBlue</span><span class="pun">&gt;</span><span class="typ">Blue</span><span class="pln"> text</span><span class="pun">&lt;/</span><span class="typ">FancyText</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">FancyText</span><span class="pln"> isBig</span><span class="pun">&gt;</span><span class="typ">Big</span><span class="pln"> text</span><span class="pun">&lt;/</span><span class="typ">FancyText</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">FancyText</span><span class="pln"> isBig isBlue</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="typ">Big</span><span class="pln"> blue text
      </span><span class="pun">&lt;/</span><span class="typ">FancyText</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	وطالما أن المكتبة styled-components ستعالج تعريف التنسيق، يمكننا استخدام أسلوب كتابة الأفعى snake case المشابه لطريقة كتابة CSS لتسمية الخصائص والواحدات (مثل % أو px)، لكن لا أهمية في الواقع للواحدات، لأن قيم خصائص التنسيق لا تقبل ذلك. للاطلاع على مزيدٍ من المعلومات عن استخدام هذه المكتبة، اقرأ <a href="https://styled-components.com/docs" rel="external nofollow">التوثيق</a> الخاص بها.
</p>

<h3>
	المكتبة React-spring
</h3>

<p>
	react-spring هي مكتبة رسوم متحركة أساسها المكتبة spring-physics، تغطي معظم احتياجاتك لواجهات مزودة برسوم متحركة، إذ تؤمن لك هذه المكتبة أدوات بالمرونة الكافية لتحويل جميع أفكارك إلى واجهات متحركة.
</p>

<p>
	تؤمن لك المكتبة React-spring واجهة برمجية ذات خطافات <a href="https://www.react-spring.io/docs/hooks/basics" rel="external nofollow">hook <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> واضحة الاستخدام لتحريك مكونات React Native.
</p>

<h3>
	المكتبة React Navigation
</h3>

<p>
	تؤمن مكتبة <a href="https://reactnavigation.org/" rel="external nofollow">React Navigation</a> التنقل والتوجه في تطبيقات React Native هي مكتبةٌ للتحكم بالتنقلات بين وجهات مختلفة في تطبيقات React Native. تتشابه في بعض النواحي مع المكتبة React Router. لكن وعلى خلاف React Router، تقدم مميزات أكثر قربًا للمنصة الأصيلة مثل الإيماءات الأصيلة والرسوميات الانتقالية التي تظهر عند التنقل بين الواجهات المختلفة للتطبيق.
</p>

<h2>
	كلمة ختامية
</h2>

<p>
	وهكذا نكون قد أكملنا تطبيقنا وأصبح جاهزًا.
</p>

<p>
	لقد تعلمنا خلال هذه الرحلة العديد من المفاهيم الجديدة، مثل إعداد تطبيقات React Native باستخدام المنصة Expo، وطريقة العمل مع مكوّنات React Native البنيوية، وتنسيق مظهر هذه المكوّنات، إضافةً إلى الاتصال مع الخادم واختبار التطبيقات. ستكون الخطوة الأخيرة هي نشر التطبيق على متجر Apple App أو متجر Google Play.
</p>

<p>
	يبقى موضوع نشر التطبيق خيارًا شخصيًا وليس أمرًا مُلحًّا، لأنك ستحتاج أيضًا إلى نشر وتوزيع الخادم <a href="https://github.com/fullstack-hy2020/rate-repository-api" rel="external nofollow">rate-repository-api</a>، أما من أجل تطبيق React Native بحد ذاته، فعليك أن تُنشئ نسخة بناء لمنصة iOS أو لمنصة أندرويد باتباع الإرشادات في <a href="https://docs.expo.io/distribution/building-standalone-apps/" rel="external nofollow">توثيق</a> Expo. وبعد ذلك ستتمكن من توزيع هذه النسخ على متجري Apple App أو Google Play، وستجد تفاصيل عن هذه الخطوة أيضًا في <a href="https://docs.expo.io/distribution/uploading-apps/" rel="external nofollow">توثيق</a> Expo.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://fullstackopen.com/en/part10/testing_and_extending_our_application" rel="external nofollow">Testing and extending our application</a> من سلسلة [Deep Dive Into Modern Web Development
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/react/%D8%AA%D9%88%D8%A7%D8%B5%D9%84-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-native-%D9%85%D8%B9-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r1677/" rel="">تواصل تطبيق React Native مع الخادم</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-react-native-r1265/" rel="">أساسيات React Native</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D8%AD%D8%B1%D9%8A%D9%83-%D9%81%D9%8A-react-native-r925/" rel="">مدخل إلى التحريك في React Native</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1678</guid><pubDate>Wed, 07 Sep 2022 16:07:00 +0000</pubDate></item><item><title>&#x62A;&#x648;&#x627;&#x635;&#x644; &#x62A;&#x637;&#x628;&#x64A;&#x642; React Native &#x645;&#x639; &#x627;&#x644;&#x62E;&#x627;&#x62F;&#x645;</title><link>https://academy.hsoub.com/programming/javascript/react/%D8%AA%D9%88%D8%A7%D8%B5%D9%84-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-native-%D9%85%D8%B9-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r1677/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/Communicating-with-server.png.03ac4bc569328ced3534ae9aca5910db.png" /></p>

<p>
	لقد أضفنا حتى الآن عدة ميزات إلى تطبيقنا لكن دون أي اتصال بالخادم، إذ يستخدم تطبيق قائمة المستودعات المقيَّمة الذي نفذناه في المقال السابق <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-react-native-r1265/" rel="">أساسيات React Native</a> -على سبيل المثال- بيانات وهمية، ولا يرسل نموذج تسجيل الدخول بيانات التحقق من المستخدم إلى أية وصلة تخديم endpoint مفوَّضة باستقبال هذه البيانات، لذا سنتعلم في هذا المقال كيف سنتصل <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">بالخادم</a> مستخدمين طلبات HTTP، وكيف سنستخدم المكتبة Apollo Client ضمن تطبيقات <a href="https://wiki.hsoub.com/ReactNative" rel="external">React Native</a>، وكذلك طريقة تخزين البيانات في جهاز المستخدم.
</p>

<p>
	سنحتاج قبل الشروع بتعلم تلك النقاط إلى خادم لنتواصل معه، لذلك فقد أنجزنا خادمًا كاملًا ووضعناه في المستودع <a href="https://github.com/fullstack-hy2020/rate-repository-api" rel="external nofollow">rate-repository-api</a>. سيلبي الخادم "rate-repository-api" كل ما تحتاجه الواجهة البرمجية لتطبيقنا خلال هذا القسم، إذ يستخدم "rate-repository-api" <a href="https://academy.hsoub.com/devops/servers/databases/%d9%83%d9%8a%d9%81-%d9%88%d9%85%d8%aa%d9%89-%d9%86%d8%b3%d8%aa%d8%ae%d8%af%d9%85-sqlite-r111/" rel="">قاعدة بيانات SQLite</a> والتي لا تحتاج إلى إعدادات، كما يؤمن واجهة برمجية خاصة بالمكتبة Apollo GraphQL، إضافةً إلى بعض وصلات التخديم التي تؤمن واجهة برمجية متوافقة مع REST.
</p>

<p>
	قبل أن تغوص أعمق في مادة هذا المقال، عليك أن تُعدَّ الخادم "rate-repository-api" باتباع الإرشادات الموجودة في ملف <a href="https://github.com/fullstack-hy2020/rate-repository-api/blob/master/README.md" rel="external nofollow">README</a> الخاص بالمستودع. ننصحك إن كنت ستستخدم المقلّد emulator لتطوير التطبيق، أن تشغله على نفس الحاسوب الذي يشغّل الخادم، فسيسهل ذلك طلبات الاتصال عبر الشبكة على نحوٍ واضح.
</p>

<h2>
	طلبات HTTP
</h2>

<p>
	تزودنا React Native بالواجهة البرمجية <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-fetch-%D9%81%D9%8A-javascript-r739/" rel="">Fetch <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a> لتنفيذ طلبات HTTP الخاصة بتطبيقنا، كما تدعم أيضًا الواجهة البرمجية القديمة <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86-xmlhttprequest-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1299/" rel="">XMLHttpRequest <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a> والتي ستمكننا من استخدام مكتبات خارجية مصدرها طرف ثالث مثل <a href="https://github.com/axios/axios" rel="external nofollow">Axios</a>. وهذه الواجهات هي نفسها التي استخدمناها في بيئة المتصفح، كما أنها متاحة لكامل التطبيق ولا حاجة لإدراجها.
</p>

<p>
	قد يتفق المطورون الذين تعاملوا مع الواجهتين البرمجيتين السابقتين، أن Fetch <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> أسهل استخدامًا وأكثر عصرية، لكن للواجهة XMLHttpRequest <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> استعمالاتها أيضًا، ولأجل تبسيط الموضوع سنتعامل فقط مع Fetch <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> في أمثلتنا.
</p>

<p>
	يمكن استخدام Fetch <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> لإرسال طلبات HTTP باستخدام الدالة <code>fetch</code> وسيكون الوسيط الأول لهذه الدالة عنوان المورد URL وطلب HTTP الافتراضي لها هو GET:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_11" style="">
<span class="pln">fetch</span><span class="pun">(</span><span class="str">'https://my-api.com/get-end-point'</span><span class="pun">);</span></pre>

<p>
	أما الوسيط الثاني للدالة فهو كائن خيارات يمكنك استعماله لإنشاء طلب HTTP آخر، أو ترويسة طلب، أو متن body طلب:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_13" style="">
<span class="pln">fetch</span><span class="pun">(</span><span class="str">'https://my-api.com/post-end-point'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
  headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Accept</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pun">,</span><span class="pln">
    </span><span class="str">'Content-Type'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  body</span><span class="pun">:</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">({</span><span class="pln">
    firstParam</span><span class="pun">:</span><span class="pln"> </span><span class="str">'firstValue'</span><span class="pun">,</span><span class="pln">
    secondParam</span><span class="pun">:</span><span class="pln"> </span><span class="str">'secondValue'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">}),</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	انتبه إلى أنّ العناوين "URL's" مصطنعة ولن تستجيب لطلباتك غالبًا.
</p>

<p>
	تعمل Fetch <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> موازنةً بالمكتبة Axios في مستوى أدنى قليلًا، فلا توجد مثلًا أية عمليات تحويل أو تفسير لمتن الطلب أو الاستجابة، فعليك مثلًا أن تحدد نوع المحتوى content-type بنفسك وأن تستخدم التابع <code>JSON.stringify</code> لتحويل متن الطلب إلى نص.
</p>

<p>
	تعيد الدالة <code>fetch</code> <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-promise-%D9%81%D9%8A-javascript-r740/" rel="">وعدًا</a> (promise) يُحَل resolves إلى كائن الاستجابة <a href="https://developer.mozilla.org/en-US/docs/Web/API/Response" rel="external nofollow">Response</a>، وانتبه إلى أنّ رموز الحالة للأخطاء التي تحصل مثل 400 أو 500 لن تُرفض كما هو الوضع في Axios مثلًا.
</p>

<p>
	يمكن أن نفسّر متن الاستجابة المنسقة بأسلوب <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">JSON</a> باستخدام التابع <code>Response.json</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_17" style="">
<span class="kwd">const</span><span class="pln"> fetchMovies </span><span class="pun">=</span><span class="pln"> async </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'https://reactnative.dev/movies.json'</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> json </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> json</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	وللاطلاع على أساسيات Fetch <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> بشيء من التفصيل، اقرأ مقال <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-fetch-%D9%81%D9%8A-javascript-r739/" rel="">الواجهة البرمجية fetch في JavaScript</a> ومقال <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84%D8%A7%D8%AA-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%84%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-fetch-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1297/" rel="">استعمالات متقدمة للواجهة البرمجية Fetch في جافاسكربت</a> وأضف إليهما مقال <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AA%D8%A8%D8%B9-%D8%AA%D9%82%D8%AF%D9%85-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D9%86%D8%B2%D9%8A%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%B9%D8%A8%D8%B1-fetch-%D9%88%D9%85%D9%82%D8%A7%D8%B7%D8%B9%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1295/" rel="">تتبع تقدم عملية تنزيل البيانات عبر Fetch ومقاطعتها في جافاسكربت</a>.
</p>

<p>
	لنبدأ بالاستخدام التطبيقي للمكتبة Fetch <abbr title="Application Programming Interface | واجهة برمجية">API</abbr>، إذ يؤمن الخادم rate-repository-api وصلة تخديم تعيد قائمةً مرقمةً من المستودعات المقيَّمة. ستتمكن من الولوج إلى وصلة التخديم الموجودة على العنوان "http://localhost:5000/api/repositories" بمجرد أن يعمل الخادم. تُرقّم البيانات بأسلوب الترقيم المعتمد على المؤشرات <a href="https://graphql.org/learn/pagination/" rel="external nofollow">cursor based pagination format</a>، وستجد البيانات الفعلية خلف المفتاح "node" في المصفوفة "edges".
</p>

<p>
	لا يمكن، لسوء الحظ، الوصول إلى الخادم rate-repository-api مباشرةً من خلال تطبيقنا بطلب العنوان "http://localhost:5000/api/repositories"، إذ نحتاج لإرسال طلب إلى وصلة التخديم هذه عبر التطبيق الوصول إلى الخادم باستخدام عنوان IP الخاص به في الشبكة المحلية التي ينتمي إليها. ولإيجاد هذا العنوان، افتح أدوات تطوير Expo بتنفيذ الأمر <code>npm start</code>. سترى في أدوات التطوير عنوانًا له بالبادئة "//:exp" فوق شيفرة QR:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="105854" href="https://academy.hsoub.com/uploads/monthly_2022_08/getting_server_ip_01.png.61f38310808eab2addfeae38d6978147.png" rel=""><img alt="getting_server_ip_01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="105854" data-unique="1p7gfb1v8" src="https://academy.hsoub.com/uploads/monthly_2022_08/getting_server_ip_01.thumb.png.bc2ba630c7260ba3e28d4d3c1cb7f396.png"></a>
</p>

<p>
	انسخ عنوان IP الموجود بين البادئة "//:exp" والنهاية ":"، وسيكون في مثالنا 192.168.100.16. أنشئ بعد ذلك <a href="https://academy.hsoub.com/programming/general/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%B9%D9%86%D9%88%D8%A7%D9%86-url-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87-r1435/" rel="">عنوان URL</a> بالتنسيق التالي "http://<ip_address>:5000/api/repositories" ثم توجه إليه من خلال المتصفح، وستحصل على نفس الاستجابة التي حصلت عليها باستخدام عنوان الخادم المحلي "localhost".</ip_address></p>

<p>
	بعد أن حددنا عنوان وصل التخديم، سنستخدم البيانات الفعلية التي يزودنا بها الخادم في تطبيق قائمة المستودعات المقيَّمة. أشرنا سابقًا أننا نستخدم بيانات وهمية مخزّنة في المتغير <code>repositories</code>. اِحذف هذا المتغير واستعض عن البيانات الوهمية في الملف "RepositoryList.jsx" الموجود في المجلد "components" بهذه الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_19" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useState</span><span class="pun">,</span><span class="pln"> useEffect </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="com">// ...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">repositories</span><span class="pun">,</span><span class="pln"> setRepositories</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">();</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> fetchRepositories </span><span class="pun">=</span><span class="pln"> async </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ‫صحح عنوان IP إن كان مختلفًا لديك</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'http://192.168.100.16:5000/api/repositories'</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> json </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">

    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">json</span><span class="pun">);</span><span class="pln">

    setRepositories</span><span class="pun">(</span><span class="pln">json</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

  useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fetchRepositories</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">},</span><span class="pln"> </span><span class="pun">[]);</span><span class="pln">

  </span><span class="com">// Get the nodes from the edges array</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> repositoryNodes </span><span class="pun">=</span><span class="pln"> repositories
    </span><span class="pun">?</span><span class="pln"> repositories</span><span class="pun">.</span><span class="pln">edges</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">edge </span><span class="pun">=&gt;</span><span class="pln"> edge</span><span class="pun">.</span><span class="pln">node</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">:</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">FlatList</span><span class="pln">
      data</span><span class="pun">={</span><span class="pln">repositoryNodes</span><span class="pun">}</span><span class="pln">
      </span><span class="com">// Other props</span><span class="pln">
    </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pun">;</span></pre>

<p>
	استخدمنا الخطاف <code>useState</code> للحفاظ على حالة قائمة المستودعات، كما استخدمنا الخطاف <code>useEffect</code> لاستدعاء الدالة <code>fetchRepositories</code>عندما يُثبّت المكوّن <code>RepositoryList</code> ضمن شجرة المكوّنات. استخلصنا المستودعات الفعلية ووضعناها في المتغير <code>repositoryNode</code> واستبدلنا به المتغير السابق <code>repositories</code> الموجود في الخاصية <code>data</code> العائدة للمكوّن <code>FlatList</code>، وبذلك يمكننا أن نرى البيانات الفعلية التي يعطيها الخادم ضمن التطبيق.
</p>

<p>
	من الجيد عادةً تسجيل استجابة الخادم لتتمكن من تحرّيها كما فعلنا في الدالة <code>fetchRepositories</code>. ينبغي أن تكون قادرًا على الاطلاع على السجلات في أدوات تطوير Expo عن طريق الوصول إلى سجلات جهازك كما تعلمنا في <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-react-native-r1265/" rel="">المقال السابق</a> (فقرة عرض سجلات العمل). إن استخدمت تطبيق جوّال Expo وأخفق طلب HTTP، تأكد أن هاتفك وحاسوبك الذي يشغّل الخادم ينتميان إلى نفس الشبكة اللاسلكية. وإن لم تستطع ذلك، استخدم المقلّد على نفس الحاسوب الذي يعمل عليه الخادم أو هيئ نفق توصيل tunnel إلى الخادم المحلي. يمكنك استخدام المكتبة <a href="https://ngrok.com/" rel="external nofollow">Ngrok</a> مثلًا لإنجاز ذلك.
</p>

<p>
	يمكن إعادة كتابة شيفرة إحضار البيانات الموجودة في المكوّن <code>RepositoryList</code>، إذ يعرف المكوّن مثلًا تفاصيل الطلبات عبر الشبكة كعنوان وصلة التخديم، وبالإضافة إلى ذلك، تحمل شيفرة إحضار البيانات إمكانية لإعادة استخدامها. لنعد إذًا كتابة شيفرة المكوّن بنقل شيفرة إحضار البيانات إلى خطاف خاص بها. سننشئ أولًا مجلدًا باسم "hooks" ضمن المجلد "src" ومن ثم سنضع فيه الملف "useRepositories.js" الذي يحتوي الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_23" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useState</span><span class="pun">,</span><span class="pln"> useEffect </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> useRepositories </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">repositories</span><span class="pun">,</span><span class="pln"> setRepositories</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">();</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">loading</span><span class="pun">,</span><span class="pln"> setLoading</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> fetchRepositories </span><span class="pun">=</span><span class="pln"> async </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setLoading</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// ‫صحح عنوان IP إن كان مختلفًا لديك</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'http://192.168.100.16:5000/api/repositories'</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> json </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">

    setLoading</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
    setRepositories</span><span class="pun">(</span><span class="pln">json</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

  useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fetchRepositories</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">},</span><span class="pln"> </span><span class="pun">[]);</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> repositories</span><span class="pun">,</span><span class="pln"> loading</span><span class="pun">,</span><span class="pln"> refetch</span><span class="pun">:</span><span class="pln"> fetchRepositories </span><span class="pun">};</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> useRepositories</span><span class="pun">;</span></pre>

<p>
	لنستخدم الآن الخطاف الجديد <code>useRepositories</code> في المكوّن <code>RepositoryList</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_25" style="">
<span class="com">// ...</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> useRepositories from </span><span class="str">'../hooks/useRepositories'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> repositories </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useRepositories</span><span class="pun">();</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> repositoryNodes </span><span class="pun">=</span><span class="pln"> repositories
    </span><span class="pun">?</span><span class="pln"> repositories</span><span class="pun">.</span><span class="pln">edges</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">edge </span><span class="pun">=&gt;</span><span class="pln"> edge</span><span class="pun">.</span><span class="pln">node</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">:</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">FlatList</span><span class="pln">
      data</span><span class="pun">={</span><span class="pln">repositoryNodes</span><span class="pun">}</span><span class="pln">
      </span><span class="com">// Other props</span><span class="pln">
    </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pun">;</span></pre>

<p>
	وهكذا لن يكون لدى المكوّن <code>RepositoryList</code> أية فكرة عن طريقة الحصول على مستودعات القائمة، وربما سنطلب هذه المستودعات مستقبلًا باستخدام الواجهة البرمجية للمكتبة GraphQL بدلًا من واجهة REST، ونرى ما الذي سيحدث.
</p>

<h2>
	العمل مع المكتبتين GraphQL و Apollo client
</h2>

<p>
	تعرفنا في <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D8%A9-graphql-server-td-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1208/" rel="">القسم 8</a> على GraphQL وكيفية إرسال استعلامات إلى خادم Apollo في تطبيقات React بالاستفادة من المكتبة <a href="https://www.apollographql.com/docs/react/" rel="external nofollow">Apollo Client</a>. ولحسن الحظ، يمكنك استخدام Apollo client في تطبيقات React Native بنفس الطريقة التي استخدمناها في بناء تطبيقات React الخاصة بالويب.
</p>

<p>
	وكما أشرنا سابقًا، سيزودنا الخادم "rate-repository-api" بواجهة برمجية للمكتبة GraphQL نُفِّذت باستخدام Apollo client، وبالتالي ستتمكن من الولوج إلى <a href="https://www.apollographql.com/docs/apollo-server/testing/graphql-playground/#gatsby-focus-wrapper" rel="external nofollow">أرضية عمل GraphQL</a> بالتوجه إلى العنوان <a href="http://localhost:5000/graphq." ipsnoembed="false" rel="external nofollow">http://localhost:5000/graphq.</a> تمثل أرضية عمل GraphQL أداة تطوير لإرسال استعلامات GraphQL، وتفقد تخطيط الواجهة البرمجية للمكتبة GraphQL، وعرض توثيق هذه المكتبة. ويفضل أن تجرب دائمًا أية استعلامات سيرسلها تطبيقك من خلال أرضية عمل GraphQL قبل تضمينها في الشيفرة. ومن الأسهل أيضًا تنقيح الأخطاء المحتملة في الاستعلامات ضمن أرضية عمل GraphQL موازنةً مع تنقيحها باستخدام التطبيق. وإن لم تكن متأكدًا من الاستعلامات المتاحة أو كيفية استخدامها، انقر على النافذة "docs" لفتح التوثيق:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="105856" href="https://academy.hsoub.com/uploads/monthly_2022_08/gql_playground_docs_02.png.8a387546c30b61a79d6fa18d2a4c9b0e.png" rel=""><img alt="gql_playground_docs_02.png" class="ipsImage ipsImage_thumbnailed" data-fileid="105856" data-unique="xjhg90ydd" src="https://academy.hsoub.com/uploads/monthly_2022_08/gql_playground_docs_02.png.8a387546c30b61a79d6fa18d2a4c9b0e.png"></a>
</p>

<p>
	سنستعمل في تطبيقات React Native المكتبة <a href="https://www.npmjs.com/package/apollo-boost" rel="external nofollow">Apollo Boost</a>، وهو أسلوب لا يتطلب أية قواعد للتهيئة zero-config للبدء باستخدام Apollo client. وللتكامل مع React، سنستخدم المكتبة <a href="https://www.apollographql.com/docs/react/api/react-hooks/" rel="external nofollow">apollo/react-hooks@</a> والتي تؤمن خطافات مثل <a href="https://www.apollographql.com/docs/react/api/react-hooks/#usequery" rel="external nofollow">useQuery</a> و <a href="https://www.apollographql.com/docs/react/api/react-hooks/#usemutation" rel="external nofollow">useMutation</a> تساعدنا في العمل مع المكتبة Apollo client. لنبدأ بتثبيت الاعتماديات اللازمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_27" style="">
<span class="pln">npm install </span><span class="lit">@apollo</span><span class="pun">/</span><span class="pln">client graphql</span></pre>

<p>
	سنحتاج قبل البدء باستخدام عميل Apollo إلى إعدادٍ جزئي لمجمّع Metro لمعالجة الملفات ذات الامتداد "cjs." التي يستخدمها عميل Apollo. لنثبّت أولًا الحزمة "expo/metro-config@" التي تحتوي على إعدادات Metro الافتراضية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_29" style="">
<span class="pln">npm install </span><span class="lit">@expo</span><span class="pun">/</span><span class="pln">metro</span><span class="pun">-</span><span class="pln">config</span></pre>

<p>
	يمكننا بعد ذلك إضافة الإعداد التالي إلى الملف "metro.config.js" في المجلد الجذري للمشروع:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_31" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> getDefaultConfig </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'@expo/metro-config'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> defaultConfig </span><span class="pun">=</span><span class="pln"> getDefaultConfig</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">);</span><span class="pln">

defaultConfig</span><span class="pun">.</span><span class="pln">resolver</span><span class="pun">.</span><span class="pln">sourceExts</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="str">'cjs'</span><span class="pun">);</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> defaultConfig</span><span class="pun">;</span></pre>

<p>
	أعِد تشغيل أدوات تطوير Expo لتطبيق الإعدادات المُجراة.
</p>

<p>
	لننشئ الآن دالةً خدميّة utility function لتكوين عميل Apollo وتهيئته على النحو المطلوب. أنشئ بدايةً المجلد "utils" ضمن المجلد "src"، وأنشئ ضمنه الملف "apolloClient.js"، ثم هيئ عميل Apollo ضمن هذا الملف ليتصل مع خادم Apollo:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_33" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ApolloClient</span><span class="pun">,</span><span class="pln"> </span><span class="typ">InMemoryCache</span><span class="pun">,</span><span class="pln"> createHttpLink </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> httpLink </span><span class="pun">=</span><span class="pln"> createHttpLink</span><span class="pun">({</span><span class="pln">
  </span><span class="com">// ‫صحح عنوان IP إن كان مختلفًا لديك</span><span class="pln">
  uri</span><span class="pun">:</span><span class="pln"> </span><span class="str">'http://192.168.100.16:4000/graphql'</span><span class="pun">,</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> createApolloClient </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ApolloClient</span><span class="pun">({</span><span class="pln">
    link</span><span class="pun">:</span><span class="pln"> httpLink</span><span class="pun">,</span><span class="pln">
    cache</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">InMemoryCache</span><span class="pun">(),</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> createApolloClient</span><span class="pun">;</span></pre>

<p>
	يستخدم عميل Apollo للاتصال بخادم Apollo العنوان URL ذاته الذي استخدمناه مع Fetch <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> ونفس رقم المنفذ 4000، ما عدا أنّ المسار هو "graphql/". وأخيرًا علينا تزويد التطبيق بعميل Apollo وذلك باستخدام سياق العمل <a href="https://www.apollographql.com/docs/react/api/react-hooks/#apolloprovider" rel="external nofollow">ApolloProvider</a>. سنضيفه إذًا إلى المكوّن App في الملف "App.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_35" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">NativeRouter</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-router-native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ApolloProvider</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Main</span><span class="pln"> from </span><span class="str">'./src/components/Main'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> createApolloClient from </span><span class="str">'./src/utils/apolloClient'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> apolloClient </span><span class="pun">=</span><span class="pln"> createApolloClient</span><span class="pun">();</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">NativeRouter</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">ApolloProvider</span><span class="pln"> client</span><span class="pun">={</span><span class="pln">apolloClient</span><span class="pun">}&gt;</span><span class="pln">        
      </span><span class="pun">&lt;</span><span class="typ">Main</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="typ">ApolloProvider</span><span class="pun">&gt;</span><span class="pln">    
    </span><span class="pun">&lt;/</span><span class="typ">NativeRouter</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span><span class="pun">;</span></pre>

<h2>
	تنظيم الشيفرة المتعلقة بالمكتبة GraphQL
</h2>

<p>
	سيعود الأمر إليك دائمًا في تنظيم الشيفرة المتعلقة بالمكتبة GraphQL ضمن تطبيقك. لكن ولأجل الحصول على هيكلية مرجعية، لنلق نظرةً على طريقة بسيطة لكنها فعالة في تنظيم هذه الشيفرة. عرّفنا في هذه الهيكلية كلًا من الاستعلامات والطفرات mutations والاجتزاءات fragments وغيرها من الكيانات التي قد نستخدمها في ملفات خاصة بها وتتواجد في نفس المجلد. إليك صورة توضح هيكلية يمكنك اعتمادها:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="105855" href="https://academy.hsoub.com/uploads/monthly_2022_08/gql_code_structure_03.png.54b5f28231ca86b5f4542abadaf7225a.png" rel=""><img alt="gql_code_structure_03.png" class="ipsImage ipsImage_thumbnailed" data-fileid="105855" data-unique="srrmcrbp6" src="https://academy.hsoub.com/uploads/monthly_2022_08/gql_code_structure_03.thumb.png.d78d242e4f88632823df9d1e2e95080b.png" style="width: 580px; height: auto;"></a>
</p>

<p>
	يمكنك استيراد معرّف القالب المجرّد <a href="https://www.apollographql.com/docs/apollo-server/api/apollo-server/#gql" rel="external nofollow">gql</a> المُستخدم في تعريف استعلامات من مكتبة "apollo/client@". وإن اتبعنا الهيكلية التي اقترحناها منذ قليل، فمن الأفضل إنشاء الملف "queries.js" في المجلد "graphql" ليحتوي الشيفرة اللازمة لتنفيذ استعلامات GraphQL في تطبيقنا. يمكن أن نخزّن كل استعلام ضمن متغّير خاص ثم نصدّره كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_39" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> gql </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> GET_REPOSITORIES </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  query </span><span class="pun">{</span><span class="pln">
    repositories </span><span class="pun">{</span><span class="pln">
      $</span><span class="pun">{</span><span class="com">/* ... */</span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">`;</span><span class="pln">

</span><span class="com">// other queries...</span></pre>

<p>
	يمكن إدراج تلك المتغيرات واستخدامها بالاستفادة من الخطاف <code>useQuery </code>على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_41" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useQuery </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> GET_REPOSITORIES </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../graphql/queries'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Component</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> data</span><span class="pun">,</span><span class="pln"> error</span><span class="pun">,</span><span class="pln"> loading </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useQuery</span><span class="pun">(</span><span class="pln">GET_REPOSITORIES</span><span class="pun">);</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	وينطبق على الطفرات ما ينطبق على الاستعلامات. طبعًا لا بدّ من تعريف الطفرات في ملف منفصل مثل "mutations.js"، ويُفضّل استخدام <a href="ttps://www.apollographql.com/docs/react/data/fragments/" rel="external nofollow">الاجتزاءات (fragments)</a> في الاستعلامات لتفادي تكرار كتابة الحقول نفسها عدة مرات.
</p>

<h2>
	تطوير هيكلية التطبيق
</h2>

<p>
	عندما يأخذ التطبيق في النمو، سيزداد حجم بعض الملفات لتصبح ربما كبيرة جدًا لإدارتها بصورةٍ صحيحة. لنفترض أنه لدينا مكوّن A سيصيّر مكونين B و C. ستُعرّف جميع هذه المكونات ضمن الملف "A.jsx" والذي سيوضع في المجلد"components". نريد الآن أن ننقل المكونين A و B إلى ملفين مستقلين "B.jsx" و "C.jsx" دون أن نجري تغييرًا كبيرُا في تنظيم الشيفرة. سيكون أمامنا في هذه الحالة خيارين:
</p>

<p>
	الأول، إنشاء الملفين "B.jsx" و"C.jsx" في المجلد "components". وستكون هيكلية التطبيق على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_43" style="">
<span class="pln">components</span><span class="pun">/</span><span class="pln">
  A</span><span class="pun">.</span><span class="pln">jsx
  B</span><span class="pun">.</span><span class="pln">jsx
  C</span><span class="pun">.</span><span class="pln">jsx
  </span><span class="pun">...</span></pre>

<p>
	الثاني، إنشاء مجلد A في المجلد "components" ثم إنشاء الملفين "B.jsx"و"C.jsx" ضمنه. ولتفادي إخفاق المكونات التي تُدرِج الملف "A.jsx" في شيفرتها، انقل الملف "A.jsx" إلى المجلد "A" وغيّر اسمه إلى "index.jsx". سينتج عن هذا الأسلوب الهيكلية التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_45" style="">
<span class="pln">components</span><span class="pun">/</span><span class="pln">
  A</span><span class="pun">/</span><span class="pln">
    B</span><span class="pun">.</span><span class="pln">jsx
    C</span><span class="pun">.</span><span class="pln">jsx
    index</span><span class="pun">.</span><span class="pln">jsx
  </span><span class="pun">...</span></pre>

<p>
	يُعد الخيار الأول مناسبًا تمامًا إن لم يكن المكونين C وB قابلين للاستخدام خارج حدود المكوّن A، فلا فائدة من وضعهما في ملفات منفصلة في المجلد "components"؛ أما الخيار الثاني فهو مناسبٌ لأسلوب الوحدات المستقلة، إذ أنه لا يسبب أية مشاكل في إدراج المكونات، لأن إدراج مسار مثل "A/." سيطابق إدراج كلا الملفين "A.jsx" و"A/index.jsx".
</p>

<h2>
	التمرين 10.11
</h2>

<h3>
	إحضار المستودعات باستخدام Apollo client
</h3>

<p>
	نريد استبدال ما نفذناه ضمن الخطاف <code>useRepositories</code> مستخدمين المكتبة Fetch <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> باستعلام GraphQL. شغّل أرضية عمل Apollo على العنوان "http://localhost:4000"، ثم افتح توثيق المكتبة بالنقر على النافذة "docs". يستخدم الاستعلام عدة وسطاء اختيارية، فلا حاجة لتحديدها الآن. كوّن استعلامًا ضمن أرضية عمل Apollo لإحضار المستودعات بالاستفادة من قيم الحقول التي تعرضها حاليًا في واجهة التطبيق. ستُرقّم النتائج التي تضم قرابة 30 نتيجة افتراضيًا. يمكنك تجاهل موضوع الترقيم حاليًا.
</p>

<p>
	عندما يعمل الاستعلام ضمن أرضية عمل Apollo، اِستخدمه ليحل مكان شيفرة المكتبة FetchAPI في الخطاف <code>useRepositories</code>. يمكن أن تنفِّذ ذلك باستخدام الخطاف <a href="https://www.apollographql.com/docs/react/api/react-hooks/#usequery" rel="external nofollow">useQuery</a>. أدرج بعدها معّرف القالب المجرّد <code>gql</code> من المكتبة بالطريقة التي شرحناها سابقًا. حاول أن تستخدم الهيكلية التي اقترحناها سابقًا لتنظيم الشيفرة المتعلقة بالمكتبة GraphQL. ولتجنب مشاكل الذاكرة الخبيئة مستقبلًا، استخدم سياسة الإحضار (<a href="https://www.apollographql.com/docs/react/data/queries/#configuring-fetch-logic" rel="external nofollow">fetch policy</a>) "cache-and-network"، التي يمكن استخدامها مع الخطاف <code>useQuery</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_47" style="">
<span class="pln">useQuery</span><span class="pun">(</span><span class="pln">MY_QUERY</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  fetchPolicy</span><span class="pun">:</span><span class="pln"> </span><span class="str">'cache-and-network'</span><span class="pun">,</span><span class="pln">
  </span><span class="com">// خيارات أخرى</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يجب أن لا تؤثر التغييرات في شيفرة الخطاف <code>useRepositories</code> على أداء المكوّن <code>RepositoryList</code> بأي شكل من الأشكال.
</p>

<h2>
	متغيرات البيئة
</h2>

<p>
	سيعمل كل تطبيق على الأرجح في أكثر من بيئة، ومن الواضح أن بيئتي التطوير والإنتاج أقرب مثالين عن الفكرة، لكن خارج هاتين البيئتين، هناك البيئة التي نشغّل عليها التطبيق حاليًا. تحتاج البيئات المختلفة عادةً اعتماديات مختلفة، فقد يستخدم الخادم المحلي الذي نتعامل معه قاعدة بيانات محليّة، بينما سيتعامل الخادم الذي سيُنشَر في بيئة الإنتاج مع قاعدة بيانات بيئة الإنتاج. ولكي نجعل شيفرتنا مستقلة عن بيئة التشغيل، لا بد من ربط هذه الاعتماديات بمعاملات أو متغيرات. لاحظ مثلًا أننا نستخدم في تطبيقنا قيمة جاهزة تعتمد بشدة على بيئة التشغيل وهي عنوان الخادم.
</p>

<p>
	تعلمنا سابقًا أنه بالإمكان تزويد البرامج قيد التشغيل بمتغيرات بيئة، إذ يمكن تعريف هذه المتغيرات من خلال سطر الأوامر أو من خلال ملف تهيئة بيئة التشغيل الذي ينتهي بلاحقة مثل "env." ومكتبات يؤمنها طرف ثالث مثل Dotenv. لكن ولسوء الحظ لا تدعم React Native الاستخدام المباشر لمتغيرات البيئة، لكن يمكننا الوصول إلى إعدادت تهيئة Expo الموجودة في الملف "app.json" في زمن التشغيل من خلال شيفرة JavaScript. وبالتالي يمكن استخدام هذه الإعدادات لتعريف المتغيرات المتعلقة بالبيئة والوصول إليها.
</p>

<p>
	نستطيع الوصول إلى إعدادات التهيئة بإدراج الثابت <code>Constants</code> من الوحدة "expo-constants" كما فعلنا ذلك عدة مرات مسبقًا. وبإدراج هذا الثابت، ستزودنا الخاصية <code>Constants.manifest</code> بكل الإعدادات. لنجرب ذلك بكتابة شيفرة ضمن المكوّن <code>App</code> وظيفتها طباعة محتويات هذه الخاصية في سجل العمل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_49" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">NativeRouter</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-router-native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ApolloProvider</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Constants</span><span class="pln"> from </span><span class="str">'expo-constants'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Main</span><span class="pln"> from </span><span class="str">'./src/components/Main'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> createApolloClient from </span><span class="str">'./src/utils/apolloClient'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> apolloClient </span><span class="pun">=</span><span class="pln"> createApolloClient</span><span class="pun">();</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Constants</span><span class="pun">.</span><span class="pln">manifest</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">NativeRouter</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">ApolloProvider</span><span class="pln"> client</span><span class="pun">={</span><span class="pln">apolloClient</span><span class="pun">}&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">Main</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="typ">ApolloProvider</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">NativeRouter</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span><span class="pun">;</span></pre>

<p>
	ستجد الآن كل إعدادات التهيئة في سجل العمل.
</p>

<p>
	تقتضي الخطوة الثانية استخدام الإعدادات لتعريف متغيّرات تعتمد على بيئة التشغيل. لنبدأ المهمة بتغير اسم الملف "app.json" إلى "app.config.json". بهذا التغيير سنصبح قادرين على استخدام شيفرة جافا سكريبت داخل ملف التهيئة. غير محتوى الملف بحيث يتحوّل الكائن:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_51" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"expo"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"rate-repository-app"</span><span class="pun">,</span><span class="pln">
    </span><span class="com">// … بقية الضبط</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إلى كائن قابل للتصدير، يحتوي على الخاصية <code>expo</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_53" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'rate-repository-app'</span><span class="pun">,</span><span class="pln">
   </span><span class="com">// … بقية الضبط</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	يتلقى الكائن <code>expo</code> الخاصية <a href="https://docs.expo.io/guides/environment-variables/#using-app-manifest-extra" rel="external nofollow">extra</a> ضمن إعدادات التهيئة، وذلك لتمرير أية إعدادات خاصة بالتطبيق. ولنرى كيف يعمل هذا الأمر، سنضيف المتغير <code>env</code> إلى إعدادات تهيئة تطبيقنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_55" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'rate-repository-app'</span><span class="pun">,</span><span class="pln">
   </span><span class="com">// … بقية الضبط</span><span class="pln">
   extra</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     env</span><span class="pun">:</span><span class="pln"> </span><span class="str">'development'</span><span class="pln">
   </span><span class="pun">},</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	أعد تشغيل أدوات تطوير Expo لتطبيق التغييرات. من المفترض أن تتغير قيمة الخاصية <code>Constans.manifest</code> بحيث تضم الخاصية <code>extra</code> التي تحتوي على إعدادات التهيئة الخاصة بتطبيقنا. يمكننا الآن الوصول إلى المتغير <code>env</code> من خلال الخاصية <code>Constants.manifest.extra.env</code>
</p>

<p>
	ولأن وضع القيم الجاهزة في الشيفرة أمر طائش، سنستخدم متغير بيئة بديلًا لذلك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_57" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'rate-repository-app'</span><span class="pun">,</span><span class="pln">
   </span><span class="com">// rest of the configuration...</span><span class="pln">
   extra</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     env</span><span class="pun">:</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">env</span><span class="pun">.</span><span class="pln">ENV</span><span class="pun">,</span><span class="pln">
   </span><span class="pun">},</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	تعلمنا سابقًا كيفية إسناد قيمة لمتغير البيئة من خلال سطر الأوامر، وذلك بتحديد اسم المتغيّر وقيمته قبل الأمر الفعلي. فعلى سبيل المثال، شغل أدوات تطوير Expo، ثم أسند القيمة "test" إلى متغير البيئة <code>ENV</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_59" style="">
<span class="pln">ENV</span><span class="pun">=</span><span class="pln">test npm start</span></pre>

<p>
	لو ألقيت نظرةً على سجل العمل، فستجد أن الخاصية <code>Constants.manifest.extra.env</code> قد تغيرت.
</p>

<p>
	يمكننا كذلك تحميل متغيرات البيئة من الملف "env." كما تعلمنا في الأقسام السابقة. لكن علينا أولًا تثبيت المكتبة <a href="https://www.npmjs.com/package/dotenv" rel="external nofollow">Dotenv</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_61" style="">
<span class="pln">npm install dotenv</span></pre>

<p>
	لنضف الآن الملف "env." إلى المجلد الجذري للمشروع على أن يحتوي ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_65" style="">
<span class="pln">ENV</span><span class="pun">=</span><span class="pln">development</span></pre>

<p>
	سندرج أخيرًا المكتبة ضمن الملف "app.config.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_63" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="str">'dotenv/config'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'rate-repository-app'</span><span class="pun">,</span><span class="pln">
   </span><span class="com">// … بقية الضبط</span><span class="pln">
   extra</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     env</span><span class="pun">:</span><span class="pln"> process</span><span class="pun">.</span><span class="pln">env</span><span class="pun">.</span><span class="pln">ENV</span><span class="pun">,</span><span class="pln">
   </span><span class="pun">},</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	أعد تشغيل أدوات تطوير Expo لتطبيق التغييرات التي أجريتها على الملف "env".
</p>

<p>
	<strong>ملاحظة</strong>: لا تضع بيانات حساسة ضمن إعدادات التطبيق، لأن المستخدم بعد تنزيل التطبيق قد يكون قادرًا من الناحية النظرية على تحليل شيفرتك وسيحصل بالتالي على هذه البيانات الحساسة.
</p>

<h2>
	التمرين 10.12
</h2>

<h3>
	متغيرات البيئة
</h3>

<p>
	استخدم متغير بيئة ضمن ملف "env." لتحديد عنوان خادم Apollo بدلًا من كتابته ضمن الشيفرة، وذلك عند تهيئة عميل Apollo. يمكنك تسمية هذا المتغير "APOLLO_URI" مثلًا.
</p>

<p>
	لا تحاول الوصول إلى متغير البيئة على النحو التالي: <code>process.env.APOLLO_URI</code> خارج الملف "app.config.js"، بل استخدم الكائن <code>Constants.manifest.extra</code> كما فعلنا في المثال السابق. كذلك لا تدرج المكتبة Dotenv خارج الملف "app.config.js" وإلا قد تواجهك المشاكل.
</p>

<h2>
	تخزين البيانات في جهاز المستخدم
</h2>

<p>
	قد تحتاج في بعض الأحيان إلى تخزين بعض البيانات في جهاز المستخدم. ومن أبرز السيناريوهات التي تضطرك إلى ذلك هو تخزين شهادة التحقق من المستخدم token لكي تكون قادرًا على الحصول عليها عندما يغلق المستخدم التطبيق ثم يعيد فتحه. لقد استخدمنا عند تطوير تطبيقات الويب الذاكرة المحلية للمتصفح من خلال الكائن <code>localStorage</code>. تؤمن Recat Native بالمقابل ذاكرة تخزين مقيمة هي الذاكرة غير المتزامنة (<a href="https://react-native-async-storage.github.io/async-storage/docs/usage" rel="external nofollow">AsyncStorage</a>).
</p>

<p>
	يمكن استخدام أمر التثبيت في Expo لتثبيت نسخة من الحزمة react<em>native</em>community/async-storage@ متوافقة مع نسخة Expo SDK المستخدمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_67" style="">
<span class="pln">expo install </span><span class="lit">@react</span><span class="pun">-</span><span class="pln">native</span><span class="pun">-</span><span class="pln">async</span><span class="pun">-</span><span class="pln">storage</span><span class="pun">/</span><span class="pln">async</span><span class="pun">-</span><span class="pln">storage</span></pre>

<p>
	تتشابه الواجهة البرمجية للحزمة <code>AsyncStorage</code> مع الواجهة البرمجية للذاكرة المحلية <code>localStorage</code> بعدة نواحٍ، فكلاهما ذاكرة تعتمد مبدأ "مفتاح-قيمة". ويكمن الاختلاف الكبير بينهما، كما يوحي الاسم، أنّ عمليات AsyncStorage غير متزامنة.
</p>

<p>
	من المفيد أن نُجري تجريدًا بسيطًا لشيفرة العمليات، طالما أن <code>AsyncStorage</code> ستعمل مع مفاتيح نصية ضمن فضاء الأسماء العام global namespace. يمكن إنجاز هذا التجريد باستخدام الأصناف <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes" rel="external nofollow">class</a>. إذ يمكن، على سبيل المثال، إنشاء صنف لبطاقة تسوق، نخزّن فيها المنتجات التي يريد أن يشتريها مستخدم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_69" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">AsyncStorage</span><span class="pln"> from </span><span class="str">'@react-native-async-storage/async-storage'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">ShoppingCartStorage</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">namespace </span><span class="pun">=</span><span class="pln"> </span><span class="str">'shoppingCart'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">namespace </span><span class="pun">=</span><span class="pln"> namespace</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  async getProducts</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> rawProducts </span><span class="pun">=</span><span class="pln"> await </span><span class="typ">AsyncStorage</span><span class="pun">.</span><span class="pln">getItem</span><span class="pun">(</span><span class="pln">
      </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">namespace</span><span class="pun">}:</span><span class="pln">products</span><span class="pun">`,</span><span class="pln">
    </span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> rawProducts </span><span class="pun">?</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">rawProducts</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  async addProduct</span><span class="pun">(</span><span class="pln">productId</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> currentProducts </span><span class="pun">=</span><span class="pln"> await </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">getProducts</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> newProducts </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[...</span><span class="pln">currentProducts</span><span class="pun">,</span><span class="pln"> productId</span><span class="pun">];</span><span class="pln">

    await </span><span class="typ">AsyncStorage</span><span class="pun">.</span><span class="pln">setItem</span><span class="pun">(</span><span class="pln">
      </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">namespace</span><span class="pun">}:</span><span class="pln">products</span><span class="pun">`,</span><span class="pln">
      JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">newProducts</span><span class="pun">),</span><span class="pln">
    </span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  async clearProducts</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    await </span><span class="typ">AsyncStorage</span><span class="pun">.</span><span class="pln">removeItem</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">namespace</span><span class="pun">}:</span><span class="pln">products</span><span class="pun">`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> doShopping </span><span class="pun">=</span><span class="pln"> async </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> shoppingCartA </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ShoppingCartStorage</span><span class="pun">(</span><span class="str">'shoppingCartA'</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> shoppingCartB </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ShoppingCartStorage</span><span class="pun">(</span><span class="str">'shoppingCartB'</span><span class="pun">);</span><span class="pln">

  await shoppingCartA</span><span class="pun">.</span><span class="pln">addProduct</span><span class="pun">(</span><span class="str">'chips'</span><span class="pun">);</span><span class="pln">
  await shoppingCartA</span><span class="pun">.</span><span class="pln">addProduct</span><span class="pun">(</span><span class="str">'soda'</span><span class="pun">);</span><span class="pln">

  await shoppingCartB</span><span class="pun">.</span><span class="pln">addProduct</span><span class="pun">(</span><span class="str">'milk'</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> productsA </span><span class="pun">=</span><span class="pln"> await shoppingCartA</span><span class="pun">.</span><span class="pln">getProducts</span><span class="pun">();</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> productsB </span><span class="pun">=</span><span class="pln"> await shoppingCartB</span><span class="pun">.</span><span class="pln">getProducts</span><span class="pun">();</span><span class="pln">

  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">productsA</span><span class="pun">,</span><span class="pln"> productsB</span><span class="pun">);</span><span class="pln">

  await shoppingCartA</span><span class="pun">.</span><span class="pln">clearProducts</span><span class="pun">();</span><span class="pln">
  await shoppingCartB</span><span class="pun">.</span><span class="pln">clearProducts</span><span class="pun">();</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

doShopping</span><span class="pun">();</span></pre>

<p>
	ومن الجيد أيضًا أن نحدد فضاء أسماء للمفاتيح في <code>AsyncStorage</code>، ذلك أنها قيم نصيّة. وفي هذا السياق، سنجد أن فضاء الأسماء هو بادئة نزوّد بها مفاتيح التخزين، إذ سيمنع استخدام فضاء الأسماء مفاتيح التخزين من التصادم مع مفاتيح أخرى للمكتبة AsyncStorage. عرّفنا فضاء الأسماء في المثال السابق على شكل وسيط للدالة البانية، كما استخدمنا التنسيق "namespace:key مفتاح: فضاء أسماء" من أجل مفاتيح التخزين.
</p>

<p>
	يمكن إضافة عنصر إلى ذاكرة التخزين مستخدمين التابع <a href="https://react-native-async-storage.github.io/async-storage/docs/api#setitem" rel="external nofollow">AsyncStorage.setItem</a>. سيكون مفتاح العنصر هو الوسيط الأول للتابع بينما ستكون قيمة المفتاح هي المعامل الثاني. وطالما أن قيمة المفتاح هي قيمة نصية، فلا بد من تحويل القيم غير النصية إلى نصية باستخدام التابع <code>JSON.stringify</code>. يُستخدَم التابع <a href="https://react-native-async-storage.github.io/async-storage/docs/api/#getitem" rel="external nofollow">AsyncStorage.getItem</a> للحصول على عنصر محدد من ذاكرة التخزين، ووسيط هذا التابع هو مفتاح العنصر. أما التابع <a href="https://react-native-async-storage.github.io/async-storage/docs/api/#removeitem" rel="external nofollow">AsyncStorage.removeItem</a> فيُستخدم لحذف عنصر من ذاكرة التخزين بتحديد مفتاحه.
</p>

<h2>
	التمرينان 10.13 - 10.14
</h2>

<h3>
	10.13: استخدام الطفرات في نموذج تسجيل الدخول
</h3>

<p>
	لا يستطيع نموذج تسجيل الدخول حاليًا فعل أي شيء حيال معلومات توثيق المستخدم التي يرسلها، لذلك سنحاول تحسين الوضع قليلًا في هذا التمرين. اطلع أولًا على توثيق الاستيثاق <a href="https://github.com/fullstack-hy2020/rate-repository-api#-authentication" rel="external nofollow">authentication documentation</a> الخاص بالخادم "rate-repository-api"، ثم اختبر الاستعلامات التي تُزوَّد بها ضمن أرضية عمل Apollo. إن لم تحتوي قاعدة البيانات على أي مستخدمين يمكن وضع بعض البيانات فيها، وستجد إرشاداتٍ لذلك في الفقرة "getting started"من الملف "README" الموجود في المستودع "rate-repository-ap".
</p>

<p>
	عندما تتفهم الطريقة التي تعمل بها استعلامات الاستيثاق، أنشئ الملف "useSignIn.js" في المجلد "hooks" وضع فيه شيفرة الخطاف<code>useSignIn</code> الذي يرسل الطفرة باستخدام الخطاف <a href="https://www.apollographql.com/docs/react/api/react-hooks/#usemutation" rel="external nofollow">useMutation</a>. تقبل الطفرة <code>authenticate</code> وسيطًا وحيدًا هو <code>credentials</code> من النوع "AuthenticateInput". يحتوي نوع عنصر الإدخال (<a href="https://graphql.org/graphql-js/mutations-and-input-types" rel="external nofollow">input type</a>) حقلين لاسم المستخدم وكلمة المرور.
</p>

<p>
	ستكون القيمة التي يعيدها الخطاف على الشكل [signIn, result]، إذ يمثل الوسيط <code>result</code> نتيجة الطفرات كما يعيدها الخطاف <code>useMutation</code>، ويمثل الوسيط <code>signIn</code> دالة تنفيذ الطفرة باستخدام وسيط هو كائن من الشكل {username, password}.
</p>

<p>
	<strong>تلميح</strong>: لا تمرر دالة الطفرة إلى القيمة المعادة مباشرةً، بل أعد دالة تستدعي دالة الطفرة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_71" style="">
<span class="kwd">const</span><span class="pln"> useSignIn </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">mutate</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useMutation</span><span class="pun">(</span><span class="com">/* mutation arguments */</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> signIn </span><span class="pun">=</span><span class="pln"> async </span><span class="pun">({</span><span class="pln"> username</span><span class="pun">,</span><span class="pln"> password </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// استدعي دالة الطفرة بالوسطاء المناسبين</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[</span><span class="pln">signIn</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">];</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	بعد إنجاز الخطاف، استخدمه في دالة الاستدعاء <code>onSubmit</code> العائدة للمكوّن <code>SignIn</code> على النحو التالي مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_73" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">SignIn</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">signIn</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useSignIn</span><span class="pun">();</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> onSubmit </span><span class="pun">=</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">values</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> username</span><span class="pun">,</span><span class="pln"> password </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> values</span><span class="pun">;</span><span class="pln">

    </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> data </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> await signIn</span><span class="pun">({</span><span class="pln"> username</span><span class="pun">,</span><span class="pln"> password </span><span class="pun">});</span><span class="pln">
      console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">data</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">e</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	يمكنك عدُّ التمرين مكتملًا عندما تُسجَّل نتيجة طفرات <code>authenticate</code> في سجل عمل التطبيق بعد إرسال بيانات النموذج. وينبغي أن تتضمن نتيجة الطفرة شهادة التحقق من المستخدم.
</p>

<h3>
	10.14: تخزين شهادة التحقق- الخطوة 1
</h3>

<p>
	بعد أن حصلنا على شهادة التحقق لا بدّ من تخزينها. أنشئ الملف "authStorage.js" في المجلد "utils" بحيث يحتوي الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_75" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">AsyncStorage</span><span class="pln"> from </span><span class="str">'@react-native-async-storage/async-storage'</span><span class="pun">;</span><span class="pln">


</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AuthStorage</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">namespace </span><span class="pun">=</span><span class="pln"> </span><span class="str">'auth'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">namespace </span><span class="pun">=</span><span class="pln"> namespace</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  getAccessToken</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// احصل على شهادة التحقق</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  setAccessToken</span><span class="pun">(</span><span class="pln">accessToken</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// أضف الشهادة إلى ذاكرة التخزين</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  removeAccessToken</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// احذف شهادة التحقق من ذاكرة التخزين</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">AuthStorage</span><span class="pun">;</span></pre>

<p>
	أنجز شيفرة التوابع التالية: <code>AuthStorage.getAccessToken</code> و <code>AuthStorage.setAccessToken</code> و <code>AuthStorage.removeAccessToken</code>. استخدم المتغير <code>namespace</code> لمنح المفاتيح فضاء أسماء كما فعلنا في المثال السابق.
</p>

<h2>
	تحسين طلبات المكتبة Apollo Client
</h2>

<p>
	بعد أن أنجزنا آلية لتخزين شهادة التحقق من المستخدم، حان الوقت لاستخدامها. هيئ ذاكرة التخزين في المكوّن <code>App</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_77" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">NativeRouter</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-router-native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ApolloProvider</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Main</span><span class="pln"> from </span><span class="str">'./src/components/Main'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> createApolloClient from </span><span class="str">'./src/utils/apolloClient'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">AuthStorage</span><span class="pln"> from </span><span class="str">'./src/utils/authStorage'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> authStorage </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AuthStorage</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> apolloClient </span><span class="pun">=</span><span class="pln"> createApolloClient</span><span class="pun">(</span><span class="pln">authStorage</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">NativeRouter</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">ApolloProvider</span><span class="pln"> client</span><span class="pun">={</span><span class="pln">apolloClient</span><span class="pun">}&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">Main</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="typ">ApolloProvider</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">NativeRouter</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span><span class="pun">;</span></pre>

<p>
	مررنا نسخةً من الصنف الذي يتعامل مع ذاكرة التخزين <code>AuthStorage</code> إلى الدالة <code>createApolloClient</code> مثل وسيط، لأننا سنرسل لاحقًا شهادة التحقق إلى خادم Apollo مع كل طلب. يتوقع خادم Apollo أن تكون شهادة التحقق ضمن ترويسة الاستيثاق وبنفس تنسيق حامل الشهادة <code>&lt;ACCESS_TOKEN&gt;</code>. يمكن تحسين طلب Apollo Client باستخدام الدالة <a href="https://www.apollographql.com/docs/react/api/link/apollo-link-context/s" rel="external nofollow">setContext</a>. لنرسل شهادة التحقق إلى خادم Apollo من خلال عميل Apollo بتعديل الدالة <code>createApolloClient</code>في الملف "apolloClient.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_79" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ApolloClient</span><span class="pun">,</span><span class="pln"> </span><span class="typ">InMemoryCache</span><span class="pun">,</span><span class="pln"> createHttpLink </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Constants</span><span class="pln"> from </span><span class="str">'expo-constants'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> setContext </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client/link/context'</span><span class="pun">;</span><span class="pln">

</span><span class="com">// ‫قد تحتاج إلى تغيير هذا بناءً على كيفية تكوين URI لخادم Apollo</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> apolloUri </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Constants</span><span class="pun">.</span><span class="pln">manifest</span><span class="pun">.</span><span class="pln">extra</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> httpLink </span><span class="pun">=</span><span class="pln"> createHttpLink</span><span class="pun">({</span><span class="pln">
  uri</span><span class="pun">:</span><span class="pln"> apolloUri</span><span class="pun">,</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> createApolloClient </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">authStorage</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> authLink </span><span class="pun">=</span><span class="pln"> setContext</span><span class="pun">(</span><span class="pln">async </span><span class="pun">(</span><span class="pln">_</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> headers </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> accessToken </span><span class="pun">=</span><span class="pln"> await authStorage</span><span class="pun">.</span><span class="pln">getAccessToken</span><span class="pun">();</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="pun">...</span><span class="pln">headers</span><span class="pun">,</span><span class="pln">
          authorization</span><span class="pun">:</span><span class="pln"> accessToken </span><span class="pun">?</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Bearer</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">accessToken</span><span class="pun">}`</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">};</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">e</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        headers</span><span class="pun">,</span><span class="pln">
      </span><span class="pun">};</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ApolloClient</span><span class="pun">({</span><span class="pln">
    link</span><span class="pun">:</span><span class="pln"> authLink</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">httpLink</span><span class="pun">),</span><span class="pln">
    cache</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">InMemoryCache</span><span class="pun">(),</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> createApolloClient</span><span class="pun">;</span></pre>

<h2>
	استخدام React Context لتوزيع البيانات
</h2>

<p>
	يبقى علينا في النهاية إجراء تكامل بين ذاكرة التخزين والخطاف <code>useSignIn</code>. ولكي ننجز ذلك لا بدّ من السماح للخطاف بولوج شهادة التحقق التي هيئنا نسخة منها في المكوّن <code>App</code>. سنستخدم الواجهة <a href="https://wiki.hsoub.com/React/context" rel="external">Context</a> التي تزودنا بها React لأداء هذه المهمة. أنشئ المجلد "contexts" ضمن المجلد "src"، ثم أنشئ ضمنه الملف "AuthStorageContext.js" وضع فيه الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_81" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">AuthStorageContext</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">React</span><span class="pun">.</span><span class="pln">createContext</span><span class="pun">();</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">AuthStorageContext</span><span class="pun">;</span></pre>

<p>
	ستتمكن الآن من استخدام المكوّن <code>AuthStorageContext.Provider</code> لتوزيع نسخة من ذاكرة التخزين على المكوّنات الأبناء ضمن سياق التنفيذ context. لنضفه إلى المكوّن <code>App</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_83" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">NativeRouter</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-router-native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ApolloProvider</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pun">;</span><span class="pln">


</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Main</span><span class="pln"> from </span><span class="str">'./src/components/Main'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> createApolloClient from </span><span class="str">'./src/utils/apolloClient'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">AuthStorage</span><span class="pln"> from </span><span class="str">'./src/utils/authStorage'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">AuthStorageContext</span><span class="pln"> from </span><span class="str">'./src/contexts/AuthStorageContext'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> authStorage </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AuthStorage</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> apolloClient </span><span class="pun">=</span><span class="pln"> createApolloClient</span><span class="pun">(</span><span class="pln">authStorage</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">NativeRouter</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">ApolloProvider</span><span class="pln"> client</span><span class="pun">={</span><span class="pln">apolloClient</span><span class="pun">}&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">AuthStorageContext</span><span class="pun">.</span><span class="typ">Provider</span><span class="pln"> value</span><span class="pun">={</span><span class="pln">authStorage</span><span class="pun">}&gt;</span><span class="pln">          
            </span><span class="pun">&lt;</span><span class="typ">Main</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="typ">AuthStorageContext</span><span class="pun">.</span><span class="typ">Provider</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="typ">ApolloProvider</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">NativeRouter</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span><span class="pun">;</span></pre>

<p>
	يمكن الوصول إلى نسخة ذاكرة التخزين في دالة الخطاف <code>useSignIn</code> باستخدام الخطاف <a href="https://wiki.hsoub.com/React/hooks_reference#useContext" rel="external">useContext</a> العائد للمكتبة React على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_85" style="">
<span class="com">// ...</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useContext </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">


</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">AuthStorageContext</span><span class="pln"> from </span><span class="str">'../contexts/AuthStorageContext'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> useSignIn </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

 </span><span class="kwd">const</span><span class="pln"> authStorage </span><span class="pun">=</span><span class="pln"> useContext</span><span class="pun">(</span><span class="typ">AuthStorageContext</span><span class="pun">);</span><span class="pln">
 </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	انتبه إلى أنه من غير الممكن الولوج إلى القيمة التي يعيدها سياق العمل باستخدام الخطاف <code>useContext</code> مالم يُستخدم هذا الخطاف في مكوّن ابن للمكون <a href="https://wiki.hsoub.com/React/context#.D8.A7.D9.84.D9.85.D8.B2.D9.88.D8.AF_Provider" rel="external">Context.Provider</a>.
</p>

<p>
	يُعد الوصول إلى نسخة ذاكرة التخزين باستخدام <code>(useContext(AuthStorageContext</code> مطولًا تمامًا ويكشف عن تفاصيل التنفيذ. دعنا نحسن هذا من خلال تنفيذ خطاف <code>useAuthStorage</code> في ملف "useAuthStorage.js" في مجلد "hooks":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_87" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useContext </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln"> 

</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">AuthStorageContext</span><span class="pln"> from </span><span class="str">'../contexts/AuthStorageContext'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> useAuthStorage </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> useContext</span><span class="pun">(</span><span class="typ">AuthStorageContext</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> useAuthStorage</span><span class="pun">;</span></pre>

<p>
	يعد تنفيذ الخطاف بسيطًا جدًا ولكنه يحسن إمكانية قراءة الخطافات والمكونات التي تستخدمها وصيانتها. يمكننا استخدام الخطاف لإعادة تشكيل خطاف <code>useSignIn</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_89" style="">
<span class="com">// ...</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> useAuthStorage from </span><span class="str">'../hooks/useAuthStorage'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> useSignIn </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> authStorage </span><span class="pun">=</span><span class="pln"> useAuthStorage</span><span class="pun">();</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	ستجعل إمكانية توزيع البيانات على المكونات الأبناء من React Context مفتاحًا لحل عدد هائل من الحالات. لتطلع أكثر على هذه الحالات، اقرأ المقالة <a href="https://kentcdodds.com/blog/how-to-use-react-context-effectively" rel="external nofollow">How to use React Context effectively</a> للمؤلف "Kent C. Dodd"، لتعرف كيف توافق بين استخدام الخطاف <a href="https://wiki.hsoub.com/React/hooks_reference#useReducer" rel="external">useReducer</a> وReact context لتنفيذ آلية لإدارة الحالة. وربما ستجد هذا مفيدًا لحل التمارين القادمة.
</p>

<h2>
	التمرينان 10.15 و 10.16
</h2>

<h3>
	10.15: تخزين شهادة التحقق- الخطوة 2
</h3>

<p>
	حسّن شيفرة الخطاف <code>useSignIn</code> لكي يخزّن شهادة التحقق التي تنتج عن الطفرة <code>authenticate</code>، بحيث لا تتغير القيمة التي يعيدها الخطاف. ينبغي أن يكون التغيير الوحيد في المكوّن <code>SignIn</code> هو تحويل المستخدم إلى واجهة عرض قائمة المستودعات بعد أن يسجل دخوله بنجاح. يمكنك إنجاز ذلك باستخدام الخطاف <a href="https://reactrouter.com/docs/en/v6#usenavigate" rel="external nofollow">useNavigate</a>.
</p>

<p>
	عليك أن تعيد ضبط مخزن عميل Apollo بعد تنفيذ الطفرة <code>authenticate</code> وتخزين شهادة التحقق في ذاكرة التخزين. سيسبب ذلك مسح الذاكرة المؤقتة وإعادة تنفيذ كل الطلبات النشطة. يمكن إنجاز ذلك باستخدام التابع <a href="https://www.apollographql.com/docs/react/v2.5/api/apollo-client/#ApolloClient.resetStore" rel="external nofollow">resetStore</a> العائد للمكتبة Apollo Client. يمكن الوصول إلى عميل Apollo عبر الخطاف <code>useSignIn</code> باستخدام الخطاف <a href="https://www.apollographql.com/docs/react/api/react-hooks/#useapolloclient" rel="external nofollow">useApolloClient</a>. وانتبه إلى أنّ ترتيب خطوات التنفيذ مهم جدًا وينبغي أن يكون على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_91" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> data </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> await mutate</span><span class="pun">(</span><span class="com">/* خيارات */</span><span class="pun">);</span><span class="pln">
await authStorage</span><span class="pun">.</span><span class="pln">setAccessToken</span><span class="pun">(</span><span class="com">/* شهادة التحقق مأخوذة من البيانات */</span><span class="pun">);</span><span class="pln">
apolloClient</span><span class="pun">.</span><span class="pln">resetStore</span><span class="pun">();</span></pre>

<h3>
	10.16: تسجيل الخروج
</h3>

<p>
	ستكون الخطوة الأخيرة هي تنفيذ ميزة تسجيل الخروج. يمكن استخدام الاستعلام <code>me</code> للتحقق من بيانات الشخص الذي سجّل دخوله. فإن كانت نتيجة الاستعلام "null" فلن يكون الشخص مخوّلًا بالوصول. شغّل أرضية عمل Apollo، ثم نفّذ الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5076_93" style="">
<span class="pun">{</span><span class="pln">
  me </span><span class="pun">{</span><span class="pln">
    id
    username
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستكون النتيجة غالبًا "null"، والسبب في ذلك أنّ أرضية عمل Apollo غير مفوّضة بالوصول. ويعني ذلك أنها لم ترسل شهادة تحقق مع الطلب. راجع <a href="https://github.com/fullstack-hy2020/rate-repository-api#-authentication" rel="external nofollow">توثيق الاستيثاق</a> لتفهم آلية استخلاص شهادة تحقق من الطفرة <code>authenticate</code>. استخدم هذه الشهادة في ترويسة الاستيثاق كما هو موّضح في التوثيق. نفّذ بعد ذلك الاستعلام <code>me</code> من جديد وسترى معلومات المستخدم الذي سجّل دخوله.
</p>

<p>
	افتح الملف "AppBar.jsx" الذي يحتوي على المكوّن<code>AppBar</code> والذي يحتوي بدوره على النافذتين "Sign in" و"Repositories". عدّل النوافذ بحيث تظهر النافذة "Sign out" إن سجل المستخدم دخوله، والنافذة "Sign in" إن سجّل خروجه. يمكنك إنجاز ذلك باستخدام الاستعلام <code>me</code> مع الخطاف <a href="https://www.apollographql.com/docs/react/api/react-hooks/#usequery" rel="external nofollow">useQuery</a>.
</p>

<p>
	من المفترض أن تُحذف شهادة التحقق من ذاكرة التخزين عند الضغط على النافذة "Sign out"، وأن يُعاد ضبط مخزن عميل Apollo باستخدام التابع <a href="https://www.apollographql.com/docs/react/v2.5/api/apollo-client/#ApolloClient.resetStore" rel="external nofollow">resetStore</a>، إذ سيُعاد تنفيذ كل الاستعلامات النشطة تلقائيًا عند استدعاء التابع <code>resetStore</code>، ويعني ذلك إعادة تنفيذ الاستعلام <code>me</code>. تذكّر أن ترتيب خطوات التنفيذ مهم جدًا، ويجب إزالة شهادة التحقق من ذاكرة التخزين قبل إعادة ضبط مخزن عميل Apollo.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://fullstackopen.com/en/part10/communicating_with_server" rel="external nofollow">Communicating with server</a> من سلسلة <a href="https://fullstackopen.com/en/" rel="external nofollow">Deep Dive Into Modern Web Development</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-react-native-r1265/" rel="">أساسيات React Native</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-react-native-r1264/" rel="">مدخل إلى React Native</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D8%AD%D8%B1%D9%8A%D9%83-%D9%81%D9%8A-react-native-r925/" rel="">مدخل إلى التحريك في React Native</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A7%D9%84%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-%D9%85%D8%B9%D8%AA%D9%85%D8%AF-%D8%B9%D9%84%D9%89-redux-r1164/" rel="">الاتصال مع الخادم في تطبيق React معتمد على Redux</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1677</guid><pubDate>Fri, 19 Aug 2022 11:22:25 +0000</pubDate></item><item><title>&#x645;&#x635;&#x627;&#x62F;&#x631; &#x62A;&#x639;&#x644;&#x645; &#x645;&#x643;&#x62A;&#x628;&#x629; React &#x644;&#x628;&#x646;&#x627;&#x621; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; &#x627;&#x644;&#x648;&#x64A;&#x628;</title><link>https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%B5%D8%A7%D8%AF%D8%B1-%D8%AA%D8%B9%D9%84%D9%85-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-react-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1575/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_05/62825666d215f_----React.png.df18da2b734df1f945b4671dfae982ef.png" /></p>

<p>
	سنزوّدك من خلال هذا المقال بقائمة من مصادر React التي يمكنك استخدامها للمضي قدمًا في مسار تعلّمك تطوير الواجهات الأمامية <a href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1071/" rel="">وبناء تطبيقات الويب</a>.
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a> ومعرفة استخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">سطر الأوامر أو الطرفية</a>.
	</li>
	<li>
		<strong>الهدف</strong>: توفير مصادر إضافية لمعرفة المزيد عن مكتبة React.
	</li>
</ul>
<h2>
	التنسيقات على مستوى المكون
</h2>

<p>
	تُعرِّف العديد من تطبيقات React تنسيقاتها على أساس كل مكوِّن بدلًا من أن تكون في صفحة تنسيقات واحدة متجانسة، إذ تتيح الأداة <code>create-react-app</code> استيراد ملفات CSS إلى <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r926/" rel="">وحدات جافاسكربت</a>، بحيث تُرسَل شيفرة <a href="https://academy.hsoub.com/programming/css/%d8%aa%d8%b9%d8%b1%d9%91%d9%81-%d8%b9%d9%84%d9%89-%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d8%a7%d8%aa-css-r70/" rel="">CSS</a> إلى المستخدِم فقط عند تصيير Render المكوِّن المقابل، وقد كان بإمكاننا في تطبيقنا مثلًا كتابة ملف Form.css مخصّص ليحتوي على تنسيقات تلك المكونات، ثم استيراد التنسيقات إلى وحداتها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_7969_11" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">Form</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'./Form'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="str">'./Form.css'</span></pre>

<p>
	تسهِّل هذه الطريقة تحديد وإدارة <a href="https://academy.hsoub.com/programming/css/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AD%D9%88%D9%8A%D9%84-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-css-%D8%A5%D9%84%D9%89-%D8%AA%D9%86%D8%B3%D9%8A%D9%82%D8%A7%D8%AA-%D9%85%D8%B1%D8%A6%D9%8A%D8%A9-r1269/" rel="">شيفرة CSS</a> المُخصَّصة لمكوِّن معيَّن، ولكنها تؤدي إلى تجزئة ملف التنسيقات عبر قاعدة شيفرتك البرمجية، كما يمكن ألّا تكون هذه التجزئة مفيدةً، إذ يُعَدّ الحدّ من مقدار الشيفرة التي ليست ذات فائدة والمُرسَلة إلى المستخدِم، أمرًا منطقيًا بالنسبة للتطبيقات الأكبر حجمًا، والتي تحتوي على مئات من العروض الفريدة والكثير من الأجزاء المتحركة، إذ يُحتمَل أن تكون لديك تنسيقات على مستوى التطبيق وتنسيقات مكونات محدَّدة مبنية فوقها.
</p>

<h2>
	أدوات تطوير React
</h2>

<p>
	استخدمنا التابع <code>console.log()‎</code> للتحقق من حالة تطبيقنا وخاصياته Props، إذ سترى بعض التحذيرات المفيدة ورسائل الخطأ التي تعطيها React في واجهة سطر الأوامر CLI وطرفية جافاسكربت في المتصفح. ولكن هناك المزيد الذي نستطيع إجراؤه هنا.
</p>

<p>
	تتيح الأداة المساعدة React DevTools فحص الأجزاء الداخلية لتطبيق React مباشرةً في المتصفح، إذ تضيف لوحةً جديدةً إلى أدوات مطور متصفحك، كما يمكنك بواسطتها فحص حالة المكوّنات المختلفة وخاصياتها، وتعديل الحالة والخاصيات لإجراء تغييرات فورية على تطبيقك، إذ تُظهر لقطة الشاشة التالية تطبيقنا النهائي كما يظهر في الأداة React DevTools:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98852" href="https://academy.hsoub.com/uploads/monthly_2022_05/01_react-devtools.png.ea239da7d04620f31285cdecc8c230ee.png" rel=""><img alt="01_react-devtools.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98852" data-unique="qnsik2fy4" src="https://academy.hsoub.com/uploads/monthly_2022_05/01_react-devtools.thumb.png.51e7106d5cfa0648e8132cd5c64fddfe.png" style="width: 650px; height: auto;"></a>
</p>

<p>
	نرى على اليسار جميع المكونات التي يتألف منها تطبيقنا بما في ذلك بعض المفاتيح الفريدة للأشياء المُصيَّرة من المصفوفات، في حين نرى على اليمين الخاصيات والخطّافات Hooks التي يستخدِمها المكوِّن <code>App</code>، ولاحظ أنّ للمكوّنات <code>Form</code> و<code>FilterButton</code> و<code>Todo</code> مسافةً بادئةً إلى اليمين، وهذا يشير إلى أنّ المكوِّن <code>App</code> هو المكوِّن الأب لها، لذا يُعَدّ هذا العرض رائعًا في التطبيقات الأكثر تعقيدًا لفهم العلاقات بين الأبناء والآباء بسهولة، كما تتوفر الأداة React DevTools في عدد من الأشكال مثل:
</p>

<ul>
<li>
		<a href="https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en" rel="external nofollow">امتداد متصفح كروم Chrome</a>.
	</li>
	<li>
		<a href="https://addons.mozilla.org/en-US/firefox/addon/react-devtools/" rel="external nofollow">امتداد متصفح فايرفوكس Firefox</a>.
	</li>
	<li>
		امتداد متصفح Chromium Edge (سيُتاح قريبًا).
	</li>
	<li>
		<a href="https://www.npmjs.com/package/react-devtools" rel="external nofollow">تطبيق مستقل يمكنك تثبيته باستخدام NPM أو Yarn</a>.
	</li>
</ul>
<p>
	جرّب تثبيت إحدى هذه الأدوات، ثم استخدمها لفحص التطبيق الذي أنشأته للتو.
</p>

<h2>
	واجهة برمجة تطبيقات السياق Context <abbr title="Application Programming Interface | واجهة برمجية">API</abbr>
</h2>

<p>
	استخدَم التطبيق الذي أنشأناه خاصيات المكوِّنات لتمرير البيانات من المكوِّن <code>App</code> إلى المكوِّنات الأبناء التي تحتاجها، إذ تُعَدّ الخاصيات طريقةً مناسبةً لمشاركة البيانات في معظم الأحيان، ولكنها ليست الأفضل دائمًا بالنسبة للتطبيقات المعقَّدة والمتداخلة كثيرًا.
</p>

<p>
	توفِّر React <a href="https://wiki.hsoub.com/React/context" rel="external">واجهة برمجة تطبيقات السياق Context <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a> بوصفها طريقةً لتوفير البيانات للمكوّنات التي تحتاجها دون تمرير الخاصيات إلى أسفل شجرة المكونات، كما يوجد <a href="https://wiki.hsoub.com/React/hooks_reference#useContext" rel="external">الخطّاف <code>useContext</code></a> الذي يسهِّل ذلك.
</p>

<h2>
	أصناف المكونات
</h2>

<p>
	يمكن بناء مكونات React باستخدام أصناف ES6 التي تُسمَّى بأصناف المكونات Class Components، إذ كانت أصناف ES6 قبل ظهور الخطّافات الطريقة الوحيدة لجلب الحالة إلى المكوّنات أو إدارة آثار التصيير الجانبية، ولا تزال الطريقة الوحيدة للتعامل مع بعض الميزات الأخرى في الحالات الطارئة، وهي شائعة جدًا في مشاريع React القديمة، كما يمكنك الاطلاع على المصادر التالية:
</p>

<ul>
<li>
		<a href="https://wiki.hsoub.com/React/state_and_lifecycle" rel="external">حالة ودورة حياة المكونات في توثيق React</a>.
	</li>
	<li>
		<a href="https://wiki.hsoub.com/React" rel="external">توثيق React</a> على موسوعة حسوب.
	</li>
	<li>
		تعلّم <a href="https://wiki.hsoub.com/JavaScript" rel="external">لغة جافاسكربت</a> من خلال توثيقها على موسوعة حسوب.
	</li>
</ul>
<h2>
	الاختبار
</h2>

<p>
	توفِّر <code>create-react-app</code> بعض الأدوات لاختبار تطبيقك، كما يغطي توثيق <code>create-react-app</code> <a href="https://create-react-app.dev/docs/running-tests/" rel="external nofollow">بعض أساسيات الاختبار</a>.
</p>

<h2>
	التوجيه
</h2>

<p>
	يُعالَج التوجيه تقليديًا باستخدام خادم وليس باستخدام تطبيق على حاسوب المستخدِم، ولكن يمكن ضبط تطبيق ويب لقراءة موقع المتصفح وتحديثه، وتصيير واجهات مستخدِم معيّنة، وهذا ما يسمى بالتوجيه من طرف العميل Client-side Routing، كما يمكن إنشاء العديد من المسارات الفريدة لتطبيقك مثل <code>‎/home</code> أو <code>‎/dashboard</code> أو <code>login/‎</code>.
</p>

<p>
	أنتج مجتمع React مكتبتين رئيسيتين للتوجيه من طرف العميل هما <a href="https://reactrouter.com/" rel="external nofollow">React Router</a> و<a href="https://reach.tech/router/" rel="external nofollow">Reach Router</a>.
</p>

<ul>
<li>
		تُعَدّ مكتبة React Router مناسبةً للتطبيقات ذات احتياجات التوجيه المعقَّدة، كما أنها تلبي بعض الحالات الطارئة بطريقة أفضل من مكتبة Reach Router، ولكن تُعَدّ React Router مكتبةً أكبر.
	</li>
	<li>
		تُعَدّ مكتبة Reach Router مناسبةً للتطبيقات الأبسط، وتدير التركيز تلقائيًا أثناء تنقل المستخدِم من صفحة إلى أخرى.
	</li>
</ul>
<p>
	تُعَدّ إدارة التركيز أمرًا ضروريًا في التوجيه من طرف العميل، إذ يمكن وقوع مستخدِمي لوحة المفاتيح في مأزق التركيز، ويمكن ألّا يكون لدى مستخدِمي قارئ الشاشة أيّ فكرة عن انتقالهم إلى صفحة جديدة، في حين تُعَدّ <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D8%A9-react-router-r1166/" rel="">مكتبة Reach Router</a> مكانًا جيدًا للبدء، لأنها أفضل من حيث إمكانية الوصول، لكن سيُدمَج هذان المشروعان في المستقبل القريب، وستكون حينها مكتبة React Router هي المشروع الباقي مع إضافة ميزات إدارة التركيز الخاصة بمكتبة Reach.
</p>

<p>
	أخيرًا، لا تنسى الرجوع إلى توثيق <a href="https://wiki.hsoub.com/React" rel="external">React</a> على موسوعة حسوب فضع هذا المرجع في جعبتك (بالإضافة إلى المراجع الأخرى التي توفرها الموسوعة) والذي ستحتاج إلى الرجوع إليه بين الحين والآخر في رحلة سيرك مع مكتبة React لبناء واجهات المواقع والتطبيقات.
</p>

<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_resources" rel="external nofollow">React resources</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-react-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-react-components-r834/" rel="">مكونات React الأساسية (React Components)</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A7%D9%84%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-react-r774/" rel="">المصطلحات المستخدمة في React</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">تعلم البرمجة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1575</guid><pubDate>Fri, 08 Jul 2022 15:01:10 +0000</pubDate></item><item><title>&#x634;&#x645;&#x648;&#x644;&#x64A;&#x629; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; React &#x644;&#x643;&#x627;&#x641;&#x629; &#x623;&#x646;&#x648;&#x627;&#x639; &#x627;&#x644;&#x645;&#x633;&#x62A;&#x62E;&#x62F;&#x645;&#x64A;&#x646;</title><link>https://academy.hsoub.com/programming/javascript/react/%D8%B4%D9%85%D9%88%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-react-%D9%84%D9%83%D8%A7%D9%81%D8%A9-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D9%8A%D9%86-r1574/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_05/628251e495b7d_---React---.png.21ce799d46bcceb7468c1bb704b090bd.png" /></p>

<p>
	سنركِّز في هذا المقال على الشمولية Accessibility (أو تترجم إلى سهولة وصول أيضًا) بما في ذلك إدارة التركيز Focus Management في <a href="https://wiki.hsoub.com/React" rel="external">React</a> التي يمكنها تحسين <a href="https://academy.hsoub.com/design/user-experience/%D9%82%D8%A7%D8%A8%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%AA%D8%AC%D8%B1%D8%A8%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-r728/" rel="">قابلية الاستخدام</a> وتقليل الارتباك لكل من مستخدمي لوحة المفاتيح فقط وقارئات الشاشة.
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a> ومعرفة استخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">سطر الأوامر أو الطرفية</a>.
	</li>
	<li>
		<strong>الهدف</strong>: تعلّم كيفية تنفيذ إمكانية وصول مستخدِمي لوحة المفاتيح في React.
	</li>
</ul>
<h2>
	مستخدمو لوحة المفاتيح
</h2>

<p>
	أنجزنا حتى الآن جميع الميزات التي أردنا تنفيذها، إذ أصبح بإمكان المستخدِم إضافة مهمة جديدة، وتحديد المهام، وإلغاء تحديدها، وحذف المهام، وتعديل أسماء المهام، وترشيح قائمة المهام جميعها أو المهام النشطة أو المهام المكتملة، إذ يمكن للمستخدِمين تنفيذ جميع هذه المهام باستخدام الفأرة، ولكن لا يمكن لمستخدِمي لوحة المفاتيح فقط الوصول إلى هذه الميزات بسهولة.
</p>

<h2>
	استكشاف مشكلة قابلية استخدام لوحة المفاتيح
</h2>

<p>
	انقر على حقل الإدخال الموجود أعلى التطبيق كما لو أنك تريد إضافة مهمة جديدة، إذ سترى خطًا سميكًا يمثل حدود العنصر outline حول حقل الإدخال، إذ تُعَدّ هذه الحدود المؤشر المرئي على تركيز المتصفح على هذا العنصر حاليًا، لذا اضغط على مفتاح <code>Tab</code> من لوحة المفاتيح، وسترى ظهور المخطط حول زر الإضافة Add تحت حقل الإدخال، إذ يدل ذلك على انتقال تركيز المتصفح.
</p>

<p>
	اضغط على مفتاح <code>Tab</code> عدة مرات، وسترى مؤشر التركيز المتقطع ينتقل بين أزرار الترشيح، واستمر في الضغط على مفتاح <code>Tab</code> إلى أن يصبح مؤشر التركيز حول زر التعديل Edit الأول، ثم اضغط على مفتاح <code>Enter</code>، إذ سيبدّل المكوِّن <code>&lt;Todo /‎&gt;</code> بين القوالب كما صمّمنا، وسترى نموذجًا يتيح تعديل اسم المهمة.
</p>

<p>
	ولكن قد تتساءل عن مكان وجود مؤشر التركيز حاليًا، إذ تُزال تمامًا العناصر التي كانت موجودةً سابقًا لاستبدال شيء آخر بها عندما نبدِّل بين القوالب في المكوِّن <code>&lt;Todo /‎&gt;</code>، وهذا يعني اختفاء العنصر الذي ركّزنا عليه سابقًا، ولا يوجد شيء نركّز عليه حاليًا، فقد يؤدي ذلك إلى إرباك مجموعة كبيرة من المستخدِمين، وخاصةً المستخدِمين الذين يعتمدون على لوحة المفاتيح أو الذين يستخدِمون قارئ الشاشة، كما يمكن تحسين تجربة مستخدِمي لوحة المفاتيح وقارئ الشاشة من خلال إدارة تركيز المتصفح بأنفسنا.
</p>

<h2>
	التركيز بين القوالب
</h2>

<p>
	إذا بدَّل المستخدِم قالب <code>&lt;Todo/‎&gt;</code> من قالب العرض إلى قالب التعديل، فيجب علينا التركيز على العنصر <code>&lt;input&gt;</code> المستخدَم لإعادة تسميته، ويجب علينا إعادة التركيز مرةً أخرى إلى زر التعديل Edit عند التبديل مرةً أخرى من قالب التعديل إلى قالب العرض.
</p>

<h3>
	استهداف العناصر
</h3>

<p>
	يجب علينا إخبار React بالعنصر الذي نريد التركيز عليه في نموذج <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">DOM</a> وكيفية العثور عليه، ويساعدنا في ذلك الخطّاف <code>useRef</code> في React الذي ينشئ كائنًا له الخاصية <code>current</code>، إذ يمكن أن تكون هذه الخاصية مرجعًا لأيّ شيء نريده، ثم يمكننا البحث عن هذا المرجع لاحقًا، وهذا مفيد للإشارة إلى <a href="https://academy.hsoub.com/programming/javascript/%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-%D9%84%D9%84%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D9%81%D9%8A-dom-r647/" rel="">عناصر DOM</a>، لذا عدِّل تعليمة الاستيراد <code>import</code> في الجزء العلوي من الملف Todo.js لتتضمن الخطّاف <code>useRef</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_8" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useRef</span><span class="pun">,</span><span class="pln"> useState </span><span class="pun">}</span><span class="pln"> from </span><span class="str">"react"</span><span class="pun">;</span></pre>

<p>
	أنشئ بعد ذلك ثابتَين جديدين بعد الخطّافات في الدالة <code>Todo()‎</code>، إذ يجب أن يكون أحد هذين الثابتين مرجعًا لزر التعديل Edit في قالب العرض والآخر لحقل التعديل في قالب التعديل.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_11" style="">
<span class="kwd">const</span><span class="pln"> editFieldRef </span><span class="pun">=</span><span class="pln"> useRef</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> editButtonRef </span><span class="pun">=</span><span class="pln"> useRef</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">);</span></pre>

<p>
	تملك هذه المراجع قيمةً افتراضيةً هي <code>null</code> لأنها ستكون بلا قيمة حين ربطها بالعناصر الخاصة بها من خلال إضافة الخاصية <code>ref</code> لكل عنصر، وضبط قيمها على كائنات <code>ref</code> المسماة بأسماء مناسبة، ويجب تعديل مربع النص <code>&lt;input&gt;</code> في قالب التعديل كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_13" style="">
<span class="pun">&lt;</span><span class="pln">input
  id</span><span class="pun">={</span><span class="pln">props</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
  className</span><span class="pun">=</span><span class="str">"todo-text"</span><span class="pln">
  type</span><span class="pun">=</span><span class="str">"text"</span><span class="pln">
  value</span><span class="pun">={</span><span class="pln">newName</span><span class="pun">}</span><span class="pln">
  onChange</span><span class="pun">={</span><span class="pln">handleChange</span><span class="pun">}</span><span class="pln">
  ref</span><span class="pun">={</span><span class="pln">editFieldRef</span><span class="pun">}</span><span class="pln">
</span><span class="pun">/&gt;</span></pre>

<p>
	يجب أن يكون زر التعديل Edit في قالب العرض كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_15" style="">
<span class="pun">&lt;</span><span class="pln">button
  type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln">
  className</span><span class="pun">=</span><span class="str">"btn"</span><span class="pln">
  onClick</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setEditing</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">)}</span><span class="pln">
  ref</span><span class="pun">={</span><span class="pln">editButtonRef</span><span class="pun">}</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="typ">Edit</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span></pre>

<h3>
	التركيز على عناصر ref باستخدام الخطاف useEffect
</h3>

<p>
	يمكننا استخدام عناصر <code>ref</code> للغرض المقصود منها عن طريق استيراد خطّاف React آخر هو <code>useEffect()‎</code> الذي سُمِّي بهذا الاسم لأنه يعمل بعد أن تصيِّر React مكونًا معينًا، وسيشغّل هذا الخطّاف أيّ أمور إضافية نريد إضافتها إلى عملية التصيير، والتي لا يمكننا تشغيلها ضمن جسم الدالة الرئيسية، إذ يُعَدّ الخطّاف <code>useEffect()‎</code> مفيدًا في الوضع الحالي لأننا لا نستطيع التركيز على عنصر إلا بعد تصيير المكوِّن <code>&lt;Todo /‎&gt;</code> ومعرفة React مكان عناصر <code>ref</code>، لذا عدِّل تعليمة الاستيراد <code>import</code> في الملف Todo.js مرةً أخرى لإضافة الخطّاف <code>useEffect</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_17" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useEffect</span><span class="pun">,</span><span class="pln"> useRef</span><span class="pun">,</span><span class="pln"> useState </span><span class="pun">}</span><span class="pln"> from </span><span class="str">"react"</span><span class="pun">;</span></pre>

<p>
	يأخذ الخطّاف <code>useEffect()‎</code> دالةً على أساس وسيط له، إذ تُنفَّذ هذه الدالة بعد تصيير المكوِّن، لذا ضع استدعاء الخطّاف <code>useEffect()‎</code> التالي قبل تعليمة <code>return</code> مباشرةً في جسم الدالة <code>Todo()‎</code>، ومرّر إليه دالةً تسجّل العبارة "side effect" في الطرفية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_20" style="">
<span class="pln">useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"side effect"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	أضف تابع <code>console.log()‎</code> آخر، لتوضيح الفرق بين عملية التصيير الرئيسية وتشغيل الشيفرة ضمن الخطّاف <code>useEffect()‎</code>، أي ضع التعليمة التالية بعد الإضافة السابقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_22" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"main render"</span><span class="pun">);</span></pre>

<p>
	افتح الآن التطبيق في متصفحك، إذ يجب أن ترى كلتا الرسالتين في الطرفية مع تكرار كل منهما ثلاث مرات، ولاحظ ظهور عبارة التصيير الرئيسي main render أولًا، ثم عبارة "side effect" ثانيًا، على الرغم من وجود عبارة "side effect" أولًا في الشيفرة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_24" style="">
<span class="pln">main render </span><span class="pun">(</span><span class="lit">3</span><span class="pun">)</span><span class="pln">                                     </span><span class="typ">Todo</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">100</span><span class="pln">
side effect </span><span class="pun">(</span><span class="lit">3</span><span class="pun">)</span><span class="pln">                                     </span><span class="typ">Todo</span><span class="pun">.</span><span class="pln">js</span><span class="pun">:</span><span class="lit">98</span></pre>

<p>
	احذف تعليمة <code>console.log("main render")‎</code> الآن، ولننتقل إلى تطبيق إدارة التركيز.
</p>

<h3>
	التركيز على حقل التعديل
</h3>

<p>
	تذكّر أننا نريد التركيز على حقل التعديل عندما ننتقل إلى قالب التعديل، لذا عدِّل الخطّاف <code>useEffect()‎</code> ليصبح كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_26" style="">
<span class="pln">useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">isEditing</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    editFieldRef</span><span class="pun">.</span><span class="pln">current</span><span class="pun">.</span><span class="pln">focus</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">},</span><span class="pln"> </span><span class="pun">[</span><span class="pln">isEditing</span><span class="pun">]);</span></pre>

<p>
	إذا كانت قيمة <code>isEditing</code> هي <code>true</code>، فستقرأ React قيمة مرجع <code>editFieldRef</code> الحالية وتنقل تركيز المتصفح إليه، كما سنمرِّر مصفوفةً إلى الخطّاف <code>useEffect()‎</code> على أساس وسيط ثان، إذ تُعَدّ هذه المصفوفة قائمةً من القيم التي يجب أن يعتمد عليها الخطّاف <code>useEffect()‎</code>، فإذا ضمّنا هذه القيم، فلن يُشغَّل الخطاف <code>useEffect()‎</code> إلا عند تغيير إحدى هذه القيم، إذ نريد تغيير التركيز عندما تتغير قيمة <code>isEditing</code> فقط، لذا انتقل إلى متصفحك الآن، وسترى انتقال التركيز إلى العنصر <code>&lt;input&gt;</code> المقابل لزر التعديل Edit عند النقر عليه.
</p>

<h3>
	نقل التركيز إلى زر التعديل مرة أخرى
</h3>

<p>
	يمكن أن يبدو نقل React للتركيز مرةً أخرى إلى زر التعديل Edit عند حفظ التعديل أو إلغائه أمرًا سهلًا للوهلة الأولى، فلنجرّب إضافة شرط إلى <a href="https://wiki.hsoub.com/React/hooks_effect" rel="external">الخطّاف useEffect</a> للتركيز على زر التعديل إذا كانت قيمة الحالة <code>isEditing</code> هي <code>false</code> مثلًا، لذا عدِّل استدعاء الخطّاف <code>useEffect()‎</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_29" style="">
<span class="pln">useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">isEditing</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    editFieldRef</span><span class="pun">.</span><span class="pln">current</span><span class="pun">.</span><span class="pln">focus</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    editButtonRef</span><span class="pun">.</span><span class="pln">current</span><span class="pun">.</span><span class="pln">focus</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">},</span><span class="pln"> </span><span class="pun">[</span><span class="pln">isEditing</span><span class="pun">]);</span></pre>

<p>
	عُد إلى متصفحك وسترى انتقال تركيزك بين تعديل العنصر <code>&lt;input&gt;</code> وزر التعديل Edit مع بدء التعديل وإنهائه، ولكن يركِّز الزر Edit في المكوِّن <code>&lt;Todo /‎&gt;</code> الأخير مباشرةً على تحميل الصفحة قبل تفاعلنا مع التطبيق، وهنا يتصرّف الخطّاف <code>useEffect()‎</code> كما صممناه تمامًا، فهو يعمل بمجرد تصيير المكوِّن، ويرى أن قيمة الحالة <code>isEditing</code> هي <code>false</code>، كما يركِّز على الزر Edit، وبما أنه يوجد ثلاث نسخ من المكوِّن <code>&lt;Todo /‎&gt;</code>، فإننا نرى التركيز على الزر Edit الأخير، إذ يجب إعادة التفكير ليتغير التركيز عندما تتغير قيمة الحالة <code>isEditing</code> فقط.
</p>

<h2>
	إدارة التركيز الصارمة
</h2>

<p>
	يجب معرفة قيمة الحالة <code>isEditing</code> ومتى تتغير هذه القيمة، لذا يجب أن نكون قادرين على قراءة قيمة <code>isEditing</code> السابقة، ويجب أن يكون منطق العمل كما يلي باستخدام الشيفرة الوهمية التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_32" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">wasNotEditingBefore </span><span class="pun">&amp;&amp;</span><span class="pln"> isEditingNow</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  focusOnEditField</span><span class="pun">()</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">wasEditingBefore </span><span class="pun">&amp;&amp;</span><span class="pln"> isNotEditingNow</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  focusOnEditButton</span><span class="pun">()</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ناقش فريق React طرقًا <a href="https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state" rel="external nofollow">للحصول على حالة المكوِّن السابقة</a>، وقدّم مثالًا عن الخطّاف المُخصَّص الذي يمكننا استخدامه لذلك.
</p>

<p>
	الصق الشيفرة التالية بالقرب من أعلى الملف Todo.js قبل الدالة <code>Todo()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_34" style="">
<span class="kwd">function</span><span class="pln"> usePrevious</span><span class="pun">(</span><span class="pln">value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> ref </span><span class="pun">=</span><span class="pln"> useRef</span><span class="pun">();</span><span class="pln">
  useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    ref</span><span class="pun">.</span><span class="pln">current </span><span class="pun">=</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> ref</span><span class="pun">.</span><span class="pln">current</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنعرِّف الآن الثابت <code>wasEditing</code> بعد الخطّافات في الجزء العلوي من الدالة <code>Todo()‎</code>، إذ نريد أن يتتبع هذا الثابت قيمة <code>isEditing</code> السابقة، لذلك سنستدعي الدالة <code>usePrevious</code> مع <code>isEditing</code> على أساس وسيط كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_36" style="">
<span class="kwd">const</span><span class="pln"> wasEditing </span><span class="pun">=</span><span class="pln"> usePrevious</span><span class="pun">(</span><span class="pln">isEditing</span><span class="pun">);</span></pre>

<p>
	يمكننا باستخدام هذا الثابت تحديث الخطّاف <code>useEffect()‎</code> لتطبيق الشفرة الوهمية التي ناقشناها سابقًاـ لذا عدِّل هذه الشيفرة لتصبح كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9988_38" style="">
<span class="pln">useEffect(() =&gt; {
  if (!wasEditing &amp;&amp; isEditing) {
    editFieldRef.current.focus();
  }
  if (wasEditing &amp;&amp; !isEditing) {
    editButtonRef.current.focus();
  }
}, [wasEditing, isEditing]);</span></pre>

<p>
	لاحظ اعتماد منطق <code>useEffect()‎</code> الآن على الثابت <code>wasEditing</code>، لذلك سنقدِّمه ضمن مصفوفة الاعتماديات، وحاول مرةً أخرى استخدام زرَي التعديل Edit والإلغاء Cancel للتبديل بين قوالب المكوِّن <code>&lt;Todo /‎&gt;</code>، إذ سترى مؤشر تركيز المتصفح يتحرك بطريقة مناسبة دون ظهور المشكلة التي ناقشناها سابقًا.
</p>

<h2>
	التركيز عندما يحذف المستخدم مهمة
</h2>

<p>
	هناك مشكلة واحدة أخيرة في تجربة لوحة المفاتيح، وهي اختفاء التركيز عندما يحذف المستخدِم مهمةً من القائمة، لذا سنتبع نمطًا مشابهًا للتعديلات السابقة، إذ سننشئ مرجعًا جديدًا، وسنستخدِم الخطّاف <code>usePrevious()‎</code>، لنتمكّن من التركيز على عنوان القائمة عندما يحذف المستخدِم مهمةً.
</p>

<p>
	يكون المكان الذي نرغب في إرسال تركيزنا إليه واضحًا في بعض الأحيان، إذ كانت لدينا نقطة أصل للرجوع إليها وهي زر التعديل Edit عند التبديل بين قوالب <code>&lt;Todo /‎&gt;</code>، لكن لا يوجد مكان للرجوع إليه في حالتنا، لأننا نزيل العناصر تمامًا من نموذج DOM، فعنوان القائمة هو أفضل خيار لأنه قريب من عنصر القائمة الذي سيحذفه المستخدِم، كما سيخبر التركيز عليه المستخدِم بعدد المهام المتبقية.
</p>

<h3>
	إنشاء المرجع
</h3>

<p>
	استورد الخطّافَين <code>useRef()‎</code> و<code>useEffect()‎</code> إلى الملف App.js كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_40" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useState</span><span class="pun">,</span><span class="pln"> useRef</span><span class="pun">,</span><span class="pln"> useEffect </span><span class="pun">}</span><span class="pln"> from </span><span class="str">"react"</span><span class="pun">;</span></pre>

<p>
	صرّح بعد ذلك عن مرجع جديد ضمن الدالة <code>App()‎</code> قبل تعليمة <code>return</code> مباشرةً كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_42" style="">
<span class="kwd">const</span><span class="pln"> listHeadingRef </span><span class="pun">=</span><span class="pln"> useRef</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">);</span></pre>

<h3>
	تحضير العنوان
</h3>

<p>
	لا تكون عناصر العنوان مثل <code>&lt;h2&gt;</code> قابلةً للتركيز عادةً، ولكن لا يُعَدّ ذلك مشكلة، إذ يمكننا جعل أيّ عنصر قابلًا للتركيز برمجيًا عن طريق إضافة السمة <code>tabindex="-1"‎</code> إليه، أي يمكن التركيز عليه باستخدام لغة جافاسكربت فقط، كما لا يمكنك الضغط على مفتاح <code>Tab</code> للتركيز على عنصر له السمة <code>tabindex="-1"‎</code> باستخدام الطريقة نفسها التي يمكنك اتباعها مع عناصر <code>&lt;button&gt;</code> أو <code>&lt;a&gt;</code>، إذ يمكن تطبيق ذلك باستخدام السمة <code>tabindex="0"‎</code>، ولكنه لا يُعَدّ مناسبًا في هذه الحالة.
</p>

<p>
	لنضِف السمة <code>tabindex</code> المكتوبة بالصورة <code>tabIndex</code> في <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B5%D9%8A%D8%BA%D8%A9-javascript-syntax-extension-jsx-r777/" rel="">صيغة JSX</a> إلى العنوان الموجود أعلى قائمة المهام مع المرجع <code>headingRef</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_44" style="">
<span class="pun">&lt;</span><span class="pln">h2 id</span><span class="pun">=</span><span class="str">"list-heading"</span><span class="pln"> tabIndex</span><span class="pun">=</span><span class="str">"-1"</span><span class="pln"> ref</span><span class="pun">={</span><span class="pln">listHeadingRef</span><span class="pun">}&gt;</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">headingText</span><span class="pun">}</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">h2</span><span class="pun">&gt;</span></pre>

<p>
	ملاحظة: تُعَدّ السمة <code>tabindex</code> رائعةً لحالات إمكانية الوصول الطارئة، ولكن يجب عليك الحرص على عدم الإفراط في استخدامها، إذ يمكنك تطبيقها على عنصر فقط عندما تكون متأكدًا تمامًا من أنّ جعله قابلًا للتركيز سيفيد المستخدِم بطريقة ما، كما يجب عليك استخدام العناصر التي يمكن التركيز عليها طبيعيًا مثل الأزرار وعناصر الروابط وحقول الإدخال، في حين يمكن أن يكون لاستخدام السمة <code>tabindex</code> غير المسؤول تأثيرًا سلبيًا عميقًا على مستخدِمي لوحة المفاتيح وقارئات الشاشة.
</p>

<h3>
	الحصول على الحالة السابقة
</h3>

<p>
	نريد التركيز على العنصر المرتبط بالمرجع باستخدام السمة <code>ref</code> عندما يحذف المستخدِم مهمةً من القائمة فقط، إذ سيتطلب ذلك الخطّاف <code>usePrevious()‎</code> الذي استخدمناه سابقًا، لذا أضف هذا الخطّاف في أعلى الملف App.js بعد عبارات الاستيراد مباشرةً كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_47" style="">
<span class="kwd">function</span><span class="pln"> usePrevious</span><span class="pun">(</span><span class="pln">value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> ref </span><span class="pun">=</span><span class="pln"> useRef</span><span class="pun">();</span><span class="pln">
  useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    ref</span><span class="pun">.</span><span class="pln">current </span><span class="pun">=</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> ref</span><span class="pun">.</span><span class="pln">current</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أضف الآن ما يلي قبل تعليمة <code>return</code> ضمن الدالة <code>App()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_49" style="">
<span class="kwd">const</span><span class="pln"> prevTaskLength </span><span class="pun">=</span><span class="pln"> usePrevious</span><span class="pun">(</span><span class="pln">tasks</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span></pre>

<p>
	استدعينا الخطّاف <code>usePrevious()‎</code> لتتبع طول حالة المهام.
</p>

<p>
	ملاحظة: بما أننا نستخدِم الآن الخطّاف <code>usePrevious()‎</code> في ملفين، فستتمثّل إعادة البناء الفعالة في نقل الدالة <code>usePrevious()‎</code> إلى ملفها، وتصديرها من هذا الملف، واستيرادها حيث تريدها.
</p>

<h3>
	استخدام الخطاف useEffect()‎ للتحكم في التركيز الرئيسي
</h3>

<p>
	يمكننا الآن إعداد الخطّاف <code>useEffect()‎</code> لتشغيله عندما يتغير عدد المهام، والذي سيركِّز على العنوان إذا كان عدد المهام أقل مما كان عليه سابقًا، أي أننا حذفنا مهمةً.
</p>

<p>
	أضف ما يلي إلى جسم الدالة <code>App()‎</code> بعد الإضافات السابقة مباشرةً:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9988_51" style="">
<span class="pln">useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">tasks</span><span class="pun">.</span><span class="pln">length </span><span class="pun">-</span><span class="pln"> prevTaskLength </span><span class="pun">===</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    listHeadingRef</span><span class="pun">.</span><span class="pln">current</span><span class="pun">.</span><span class="pln">focus</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">},</span><span class="pln"> </span><span class="pun">[</span><span class="pln">tasks</span><span class="pun">.</span><span class="pln">length</span><span class="pun">,</span><span class="pln"> prevTaskLength</span><span class="pun">]);</span></pre>

<p>
	نحاول التركيز فقط على عنوان القائمة إذا كانت لدينا مهام أقل مما كانت عليه سابقًا، إذ تضمن الاعتماديات Dependencies االمُمرَّرة في هذا الخطّاف محاولة إعادة التشغيل فقط عندما تتغير أيّ من هذه القيم، أي عدد المهام الحالية أو عدد المهام السابقة، وسترى الآن ظهور مخطط التركيز المنقط حول العنوان أعلى القائمة عندما تحذف مهمةً في متصفحك.
</p>

<h2>
	الخلاصة
</h2>

<p>
	انتهينا من إنشاء تطبيق React من الألف إلى الياء، وستكون المهارات التي تعلمتها أساسًا للبناء عليها لتواصل العمل مع React، كما يمكنك أن تكون مساهمًا فعالًا في مشروع React حتى إذا كان كل ما تفعله هو التفكير مليًا في المكونات وحالتها وخاصياتها، وتذكّر أن تكتب دائمًا أفضل شيفرة HTML ممكنة.
</p>

<p>
	يُعَدّ الخطّافان <code>useRef()‎</code> و<code>useEffect()‎</code> ميزات متقدمةً إلى حد ما، ويجب أن تفخر بنفسك لاستخدامها، وابحث عن فرص لممارستها أكثر، مما سيسمح لك بإنشاء تجارب شاملة للمستخدِمين، إذ يمكن تعذُّر الوصول إلى تطبيقنا لمستخدِمي لوحة المفاتيح بدونهما.
</p>

<p>
	ملاحظة: إذا كنت بحاجة إلى التحقق من شيفرتك مقابل نسختنا من الشيفرة، فيمكنك العثور على نسخة نهائية من نموذج شيفرة تطبيق React في <a href="https://github.com/mdn/todo-react" rel="external nofollow">المستودع todo-reaction</a>. كما يمكنك الحصول على إصدار مباشر قيد التشغيل من خلال مراجعة <a href="https://mdn.github.io/todo-react-build/" rel="external nofollow">todo-react-build</a>.
</p>

<p>
	سنقدِّم في المقال التالي قائمةً بموارد React التي يمكنك استخدامها للمضي قدمًا في مسار تعلّمك.
</p>

<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_accessibility" rel="external nofollow">Accessibility in React</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A7%D9%84%D8%AA%D9%81%D8%A7%D8%B9%D9%84-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D9%8A%D9%84-%D9%88%D8%A7%D9%84%D8%AA%D8%B1%D8%B4%D9%8A%D8%AD-%D9%88%D8%A7%D9%84%D8%AA%D8%B5%D9%8A%D9%8A%D8%B1-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A-r1573/" rel="">تنفيذ التفاعل في تطبيق React: التعديل والترشيح والتصيير الشرطي</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%82%D8%A7%D8%A6%D9%85%D8%A9-%D9%85%D9%87%D8%A7%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-react-r1570/" rel="">إنشاء تطبيق قائمة مهام باستخدام React</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%AA%D9%82%D8%B3%D9%8A%D9%85-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-%D8%A5%D9%84%D9%89-%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-r1571/" rel="">تقسيم تطبيق React إلى مكونات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1071/" rel="">أساسيات بناء تطبيقات الويب</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1574</guid><pubDate>Wed, 06 Jul 2022 15:09:02 +0000</pubDate></item><item><title>&#x62A;&#x646;&#x641;&#x64A;&#x630; &#x627;&#x644;&#x62A;&#x641;&#x627;&#x639;&#x644; &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642; React: &#x627;&#x644;&#x62A;&#x639;&#x62F;&#x64A;&#x644; &#x648;&#x627;&#x644;&#x62A;&#x631;&#x634;&#x64A;&#x62D; &#x648;&#x627;&#x644;&#x62A;&#x635;&#x64A;&#x64A;&#x631; &#x627;&#x644;&#x634;&#x631;&#x637;&#x64A;</title><link>https://academy.hsoub.com/programming/javascript/react/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A7%D9%84%D8%AA%D9%81%D8%A7%D8%B9%D9%84-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D9%8A%D9%84-%D9%88%D8%A7%D9%84%D8%AA%D8%B1%D8%B4%D9%8A%D8%AD-%D9%88%D8%A7%D9%84%D8%AA%D8%B5%D9%8A%D9%8A%D8%B1-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A-r1573/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_05/62824d7bf0941_---.png.a7079837e6fbc72765949b08afcb362c.png" /></p>

<p>
	سنضيف في هذا المقال اللمسات الأخيرة على وظائف تطبيق قائمة المهام Todo List الرئيسية -الذي بنيناه في <a href="%D8%B1%D8%A7%D8%A8%D8%B7" rel="">المقالات السابقة</a>- من خلال السماح بتعديل المهام الحالية، وترشيح Filtering قائمة المهام جميعها والمهام المكتملة وغير المكتملة فقط، كما سنتعرّف على التصيير الشرطي Conditional Rendering لواجهة المستخدِم UI.
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a> ومعرفة استخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">سطر الأوامر أو الطرفية</a>.
	</li>
	<li>
		<strong>الهدف</strong>: التعرف على التصيير الشرطي في React وتطبيق ترشيح القائمة وتعديل واجهة مستخدِم في تطبيقنا.
	</li>
</ul>
<h2>
	تعديل اسم المهمة
</h2>

<p>
	لا توجد لدينا واجهة مستخدِم لتعديل اسم المهمة حتى الآن، ولكن يمكننا على الأقل تنفيذ الدالة <code>editTask()‎</code> في الملف App.js حاليًا، والتي تشبه الدالة <code>deleteTask()‎</code> لأنها ستأخذ معرِّفًا <code>id</code> للعثور على الكائن الهدف، وستأخذ الخاصية <code>newName</code> التي تحتوي على الاسم الذي سنعدّل اسم المهمة إليه، في حين أننا سنستخدِم التابع <code>Array.prototype.map()‎</code> بدلًا من التابع <code>Array.prototype.filter()‎</code> لأننا نريد إعادة مصفوفة جديدة مع بعض التعديلات بدلًا من حذف شيء منها، لذا أضف الدالة <code>editTask()‎</code> ضمن المكوِّن <code>App</code> مع الدوال الأخرى كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_8" style="">
<span class="kwd">function</span><span class="pln"> editTask</span><span class="pun">(</span><span class="pln">id</span><span class="pun">,</span><span class="pln"> newName</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> editedTaskList </span><span class="pun">=</span><span class="pln"> tasks</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">task </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// إذا كانت هذه المهمة لها معرّف المهمة المعدّلة نفسه</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">id </span><span class="pun">===</span><span class="pln"> task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">//</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{...</span><span class="pln">task</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> newName</span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> task</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  setTasks</span><span class="pun">(</span><span class="pln">editedTaskList</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	مرِّر <code>editTask</code> إلى مكونات <code>&lt;Todo /‎&gt;</code> بوصفها خاصيةً Prop بالطريقة نفسها التي مررنا بها الخاصية <code>deleteTask</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_10" style="">
<span class="kwd">const</span><span class="pln"> taskList </span><span class="pun">=</span><span class="pln"> tasks</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">task </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln">
    id</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
    name</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
    completed</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln">
    key</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
    toggleTaskCompleted</span><span class="pun">={</span><span class="pln">toggleTaskCompleted</span><span class="pun">}</span><span class="pln">
    deleteTask</span><span class="pun">={</span><span class="pln">deleteTask</span><span class="pun">}</span><span class="pln">
    editTask</span><span class="pun">={</span><span class="pln">editTask</span><span class="pun">}</span><span class="pln">
  </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">));</span></pre>

<p>
	افتح الآن الملف Todo.js، إذ سنُجري إعادة بناء.
</p>

<h2>
	واجهة مستخدم التعديل
</h2>

<p>
	يجب توفير واجهة مستخدِم للمستخدِمين للسماح لهم بتعديل مهمة، لذا استورد أولًا الخطّاف <code>useState</code> إلى المكوِّن <code>Todo</code> كما فعلنا سابقًا مع المكوِّن <code>App</code> عن طريق تعديل تعليمة الاستيراد الأولى إلى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_13" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useState </span><span class="pun">}</span><span class="pln"> from </span><span class="str">"react"</span><span class="pun">;</span></pre>

<p>
	سنضبط الآن الحالة <code>isEditing</code> التي يجب أن تكون قيمتها الافتراضية <code>false</code>، لذا أضف السطر التالي في الجزء العلوي من تعريف المكون <code>Todo(props) { … }‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_15" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">isEditing</span><span class="pun">,</span><span class="pln"> setEditing</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">);</span></pre>

<p>
	كما يجب إعادة التفكير في المكوِّن <code>&lt;Todo /‎&gt;</code> من الآن فصاعدًا، إذ نريد منه عرض أحد القالبَين التاليين بدلًا من القالب الوحيد المستخدَم حتى الآن:
</p>

<ul>
<li>
		قالب العرض View عند عرض المهام فقط، وهو ما استخدمناه حتى الآن.
	</li>
	<li>
		قالب التعديل Editing عند تعديل المهام، وسننشئه بعد قليل.
	</li>
</ul>
<p>
	انسخ الشيفرة التالية في الدالة <code>Todo()‎</code> بعد الخطّاف <code>useState()‎</code> وقبل التعليمة <code>return</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_17" style="">
<span class="kwd">const</span><span class="pln"> editingTemplate </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">form className</span><span class="pun">=</span><span class="str">"stack-small"</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"form-group"</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">label className</span><span class="pun">=</span><span class="str">"todo-label"</span><span class="pln"> htmlFor</span><span class="pun">={</span><span class="pln">props</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}&gt;</span><span class="pln">
        </span><span class="typ">New</span><span class="pln"> name </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">label</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">input id</span><span class="pun">={</span><span class="pln">props</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"todo-text"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"text"</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"btn-group"</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn todo-cancel"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="typ">Cancel</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="pln">renaming </span><span class="pun">{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"submit"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn btn__primary todo-edit"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="typ">Save</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="kwd">new</span><span class="pln"> name </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> viewTemplate </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"stack-small"</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"c-cb"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">input
          id</span><span class="pun">={</span><span class="pln">props</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
          type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pln">
          defaultChecked</span><span class="pun">={</span><span class="pln">props</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln">
          onChange</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> props</span><span class="pun">.</span><span class="pln">toggleTaskCompleted</span><span class="pun">(</span><span class="pln">props</span><span class="pun">.</span><span class="pln">id</span><span class="pun">)}</span><span class="pln">
        </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">label className</span><span class="pun">=</span><span class="str">"todo-label"</span><span class="pln"> htmlFor</span><span class="pun">={</span><span class="pln">props</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}&gt;</span><span class="pln">
          </span><span class="pun">{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">label</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"btn-group"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">Edit</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button
          type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln">
          className</span><span class="pun">=</span><span class="str">"btn btn__danger"</span><span class="pln">
          onClick</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> props</span><span class="pun">.</span><span class="pln">deleteTask</span><span class="pun">(</span><span class="pln">props</span><span class="pun">.</span><span class="pln">id</span><span class="pun">)}</span><span class="pln">
        </span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">Delete</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	أصبح لدينا الآن بنيتا قوالب مختلفتين -"Edit" و"View"- معرّفتان ضمن ثابتين منفصلين، وهذا يعني أنّ التعليمة <code>return</code> الخاصة بالمكوِّن <code>&lt;Todo /‎&gt;</code> مكررة، فهي تحتوي على تعريف قالب العرض View أيضًا، ويمكن تنظيف هذا التكرار باستخدام التصيير الشرطي Conditional Rendering لتحديد القالب الذي يعيده المكوِّن، وبالتالي يُصيَّر في <a href="https://academy.hsoub.com/design/user-interface/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-ui-r652/" rel="">واجهة المستخدِم</a>.
</p>

<h2>
	التصيير الشرطي
</h2>

<p>
	يمكننا في <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B5%D9%8A%D8%BA%D8%A9-javascript-syntax-extension-jsx-r777/" rel="">صيغة JSX</a> استخدام شرط لتغيير ما يصيّره المتصفح، إذ يُكتَب الشرط في صيغة JSX باستخدام معامِل ثلاثي Ternary Operator، فالشرط في حالة المكوِّن <code>&lt;Todo /‎&gt;</code> هو "هل تُعدَّل هذه المهمة؟"، لذا عدِّل التعليمة <code>return</code> ضمن الدالة <code>Todo()‎</code>، بحيث تصبح كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6324_20" style="">
<span class="pln">return </span><span class="tag">&lt;li</span><span class="pln"> </span><span class="atn">className</span><span class="pun">=</span><span class="atv">"todo"</span><span class="tag">&gt;</span><span class="pln">{isEditing ? editingTemplate : viewTemplate}</span><span class="tag">&lt;/li&gt;</span><span class="pln">;</span></pre>

<p>
	يجب أن يصيّر متصفحك جميع مهامك كما كانت سابقًا، ويمكن مشاهدة قالب التعديل من خلال تغيير القيمة الافتراضية للحالة <code>isEditing</code> من <code>false</code> إلى <code>true</code> في شيفرتك حاليًا، إذ سنجعل زر التعديل Edit يبدِّل هذه القيمة لاحقًا.
</p>

<h2>
	التبديل بين قوالب <todo></todo>
</h2>

<p>
	سنجعل الآن ميزة التعديل تفاعليةً، إذ يجب أولًا استدعاء الدالة <code>setEditing()‎</code> مع القيمة <code>true</code> عندما يضغط المستخدِم على زر التعديل Edit في قالب العرض <code>viewTemplate</code> لنتمكّن من التبديل بين القوالب، لذا عدِّل زر التعديل Edit في قالب العرض <code>viewTemplate</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_22" style="">
<span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn"</span><span class="pln"> onClick</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setEditing</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">)}&gt;</span><span class="pln">
  </span><span class="typ">Edit</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span></pre>

<p>
	سنضيف الآن السمة <code>onClick</code> نفسها إلى زر الإلغاء Cancel في قالب التعديل <code>editingTemplate</code>، ولكن سنضبط الحالة <code>isEditing</code> هذه المرة على القيمة <code>false</code> لتعيدنا إلى قالب العرض، لذا عدِّل زر الإلغاء Cancel في قالب التعديل <code>editingTemplate</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_24" style="">
<span class="pun">&lt;</span><span class="pln">button
  type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln">
  className</span><span class="pun">=</span><span class="str">"btn todo-cancel"</span><span class="pln">
  onClick</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setEditing</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">)}</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="typ">Cancel</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="pln">renaming </span><span class="pun">{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span></pre>

<p>
	يجب أن تكون قادرًا الآن على الضغط على زرَي التعديل Edit والإلغاء Cancel في عناصر المهام للتبديل بين القالبين.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98849" href="https://academy.hsoub.com/uploads/monthly_2022_05/01_edit.png.a3ee38f2021e71ddab8f64ad854330b8.png" rel=""><img alt="01_edit.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98849" data-unique="7cy7o3vz1" src="https://academy.hsoub.com/uploads/monthly_2022_05/01_edit.png.a3ee38f2021e71ddab8f64ad854330b8.png" style="width: 400px; height: auto;"></a>
</p>

<p>
	الخطوة التالية هي جعل وظيفة التعديل تعمل فعليًا.
</p>

<h2>
	التعديل من واجهة المستخدم
</h2>

<p>
	ما سنعمل عليه تاليًا مماثل لما عملنا عليه مع المكون <code>Form</code> (انظر المقال السابق <a href="%D8%B1%D8%A7%D8%A8%D8%B7" rel="">تقسيم تطبيق React إلى مكونات</a>).
</p>

<p>
	إذا كتب المستخدِم شيئًا في حقل الإدخال الجديد، فيجب تتبّع النص الذي يدخله، ويجب استخدام خاصية رد النداء Callback Prop بمجرد إرسال النموذج لتحديث الحالة باسم المهمة الجديد، إذ سنبدأ بإنشاء خطّاف Hook جديد لتخزين وضبط الاسم الجديد، لذا ضع ما يلي أسفل الخطّاف الموجود مسبقًا في الملف Todo.js:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_27" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">newName</span><span class="pun">,</span><span class="pln"> setNewName</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">''</span><span class="pun">);</span></pre>

<p>
	أنشئ بعد ذلك الدالة <code>handleChange()‎</code> التي تضبط الاسم الجديد، لذا ضع ما يلي بعد الخطافات وقبل القوالب:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_29" style="">
<span class="kwd">function</span><span class="pln"> handleChange</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  setNewName</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">target</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنعدِّل الآن الحقل <code>&lt;input /‎&gt;</code> الخاص بقالب التعديل <code>editingTemplate</code> بضبط السمة <code>value</code> على <code>newName</code> وربط الدالة <code>handleChange()‎</code> مع الحدث <code>onChange</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_31" style="">
<span class="pun">&lt;</span><span class="pln">input
  id</span><span class="pun">={</span><span class="pln">props</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
  className</span><span class="pun">=</span><span class="str">"todo-text"</span><span class="pln">
  type</span><span class="pun">=</span><span class="str">"text"</span><span class="pln">
  value</span><span class="pun">={</span><span class="pln">newName</span><span class="pun">}</span><span class="pln">
  onChange</span><span class="pun">={</span><span class="pln">handleChange</span><span class="pun">}</span><span class="pln">
</span><span class="pun">/&gt;</span></pre>

<p>
	يجب أخيرًا إنشاء دالة تعالِج الحدث <code>onSubmit</code> الخاصة بنموذج التعديل، لذا أضف ما يلي مباشرةً بعد الدالة السابقة التي أضفتها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_33" style="">
<span class="kwd">function</span><span class="pln"> handleSubmit</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  e</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">();</span><span class="pln">
  props</span><span class="pun">.</span><span class="pln">editTask</span><span class="pun">(</span><span class="pln">props</span><span class="pun">.</span><span class="pln">id</span><span class="pun">,</span><span class="pln"> newName</span><span class="pun">);</span><span class="pln">
  setNewName</span><span class="pun">(</span><span class="str">""</span><span class="pun">);</span><span class="pln">
  setEditing</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تذكّر أنّ خاصية رد النداء <code>editTask()‎</code> تحتاج إلى معرِّف المهمة التي نعدّلها بالإضافة إلى اسمها الجديد.
</p>

<p>
	اربط الدالة <code>handleSubmit()‎</code> مع حدث إرسال <code>submit</code> النموذج عبر إضافة معالج الحدث <code>onSubmit</code> التالي إلى عنصر النموذج <code>&lt;form&gt;</code> الخاص بقالب التعديل <code>editingTemplate</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_35" style="">
<span class="pun">&lt;</span><span class="pln">form className</span><span class="pun">=</span><span class="str">"stack-small"</span><span class="pln"> onSubmit</span><span class="pun">={</span><span class="pln">handleSubmit</span><span class="pun">}&gt;</span></pre>

<p>
	يجب الآن أن تكون قادرًا على تعديل مهمة في متصفحك.
</p>

<h2>
	العودة إلى أزرار الترشيح
</h2>

<p>
	اكتملت الآن ميزاتنا الرئيسية، وبالتالي يمكننا التفكير في أزرار الترشيح التي تكرّر العنوان "All" حاليًا بدون وظائف تطبّقها، إذ سنعيد تطبيق بعض المهارات التي استخدمناها في المكوِّن <code>&lt;Todo /‎&gt;</code> من أجل ما يلي:
</p>

<ul>
<li>
		إنشاء خطّاف لتخزين المرشِّح Filter النشط.
	</li>
	<li>
		تصيير مصفوفة من عناصر <code>&lt;FilterButton /‎&gt;</code> التي تسمح للمستخدِمِين بتغيير المرشِّح النشط بين جميع المهام والمهام المكتملة والمهام غير المكتملة.
	</li>
</ul>
<h3>
	إضافة خطاف ترشيح
</h3>

<p>
	أضف خطّافًا جديدًا إلى الدالة <code>App()‎</code> التي تقرأ وتضبط المرشِّح، إذ نريد أن يكون المرشّح الافتراضي هو <code>All</code> لأنه يجب عرض جميع المهام في البداية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_37" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">filter</span><span class="pun">,</span><span class="pln"> setFilter</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">'All'</span><span class="pun">);</span></pre>

<h3>
	تعريف المرشحات
</h3>

<p>
	هدفنا الآن هو:
</p>

<ul>
<li>
		يجب أن يكون لكل مرشِّح اسم فريد.
	</li>
	<li>
		يجب أن يكون لكل مرشِّح سلوك فريد.
	</li>
</ul>
<p>
	يُعَدّ كائن JavaScript طريقةً رائعةً لربط الأسماء بالسلوكيات، فالمفتاح هو اسم المرشِّح، والخاصية هي السلوك المرتبط بهذا الاسم.
</p>

<p>
	أضِف كائنًا بالاسم <code>FILTER_MAP</code> في الجزء العلوي من الملف App.js بعد تعليمات الاستيراد وقبل الدالة <code>App()‎</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_39" style="">
<span class="kwd">const</span><span class="pln"> FILTER_MAP </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">All</span><span class="pun">:</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">Active</span><span class="pun">:</span><span class="pln"> task </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">!</span><span class="pln">task</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">Completed</span><span class="pun">:</span><span class="pln"> task </span><span class="pun">=&gt;</span><span class="pln"> task</span><span class="pun">.</span><span class="pln">completed
</span><span class="pun">};</span></pre>

<p>
	تُعَدّ قيم الكائن <code>FILTER_MAP</code> دوالًا سنستخدِمها لترشيح مصفوفة بيانات المهام <code>tasks</code> كما يلي:
</p>

<ul>
<li>
		يعرِض المرشِّح <code>All</code> جميع المهام، لذلك يعيد القيمة <code>true</code> لجميع المهام.
	</li>
	<li>
		يعرِض المرشِّح <code>Active</code> المهام التي يكون فيها للخاصية <code>completed</code> القيمة <code>false</code>.
	</li>
	<li>
		يعرِض المرشِّح <code>Completed</code> المهام التي يكون فيها للخاصية <code>completed</code> القيمة <code>true</code>.
	</li>
</ul>
<p>
	أضف ما يلي بعد الذي أضفناه منذ قليل، إذ سنستخدِم التابع <code>Object.keys()‎</code> لتجميع مصفوفة <code>FILTER_NAMES</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_41" style="">
<span class="kwd">const</span><span class="pln"> FILTER_NAMES </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">keys</span><span class="pun">(</span><span class="pln">FILTER_MAP</span><span class="pun">);</span></pre>

<p>
	ملاحظة: نعرِّف هذه الثوابت خارج الدالة <code>App()‎</code> لأنها إذا عُرِّفت ضمنها، فسيُعاد حسابها في كل مرة يعاد فيها تصيير المكوِّن <code>&lt;App /‎&gt;</code>، ولا نريد حصول ذلك، فلن تتغير هذه المعلومات أبدًا بغض النظر عمّا يفعله تطبيقنا.
</p>

<h3>
	تصيير المرشحات
</h3>

<p>
	أصبح لدينا الآن مصفوفة <code>FILTER_NAMES</code>، وبالتالي يمكننا استخدامها لتصيير المرشِّحات الثلاثة، إذ يمكننا إنشاء ثابت يسمى <code>filterList</code> ضمن الدالة <code>App()‎</code>، إذ سنستخدِم هذا الثابت لربط مصفوفة الأسماء وإعادة المكوِّن <code>&lt;FilterButton /‎&gt;</code>، وتذكّر أننا هنا بحاجة إلى مفاتيح أيضًا، لذا أضف ما يلي بعد التصريح عن الثابت <code>taskList</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_43" style="">
<span class="kwd">const</span><span class="pln"> filterList </span><span class="pun">=</span><span class="pln"> FILTER_NAMES</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">name </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">FilterButton</span><span class="pln"> key</span><span class="pun">={</span><span class="pln">name</span><span class="pun">}</span><span class="pln"> name</span><span class="pun">={</span><span class="pln">name</span><span class="pun">}/&gt;</span><span class="pln">
</span><span class="pun">));</span></pre>

<p>
	سنستبدل الآن الثابت <code>filterList</code> بالمكونات <code>&lt;FilterButton /‎&gt;</code> الثلاثة المكرَّرة في الملف App.js، أي بدلًا مما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_45" style="">
<span class="pun">&lt;</span><span class="typ">FilterButton</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="typ">FilterButton</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="typ">FilterButton</span><span class="pln"> </span><span class="pun">/&gt;</span></pre>

<p>
	ضع التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_47" style="">
<span class="pun">{</span><span class="pln">filterList</span><span class="pun">}</span></pre>

<p>
	لن يعمل ذلك الآن، إذ لدينا المزيد لعمله أولًا.
</p>

<h3>
	المرشحات التفاعلية
</h3>

<p>
	يمكنك جعل أزرار المرشِّحات تفاعليةً من خلال تحديد الخاصيات التي تحتاج إلى استخدامها.
</p>

<ul>
<li>
		نعلم أن المكوِّن <code>&lt;FilterButton /‎&gt;</code> يجب أن يبلّغ عمّا إذا كان مضغوطًا حاليًا، ويجب الضغط عليه إذا تطابق اسمه مع القيمة الحالية لحالة المرشِّح.
	</li>
	<li>
		نعلم أن المكوِّن <code>&lt;FilterButton /‎&gt;</code> يحتاج إلى دالة رد نداء لضبط المرشِّح النشط، كما يمكننا الاستفادة مباشرةً من الخطّاف <code>setFilter</code>.
	</li>
</ul>
<p>
	عدِّل الثابت <code>filterList</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_49" style="">
<span class="kwd">const</span><span class="pln"> filterList </span><span class="pun">=</span><span class="pln"> FILTER_NAMES</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">name </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">FilterButton</span><span class="pln">
    key</span><span class="pun">={</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
    name</span><span class="pun">={</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
    isPressed</span><span class="pun">={</span><span class="pln">name </span><span class="pun">===</span><span class="pln"> filter</span><span class="pun">}</span><span class="pln">
    setFilter</span><span class="pun">={</span><span class="pln">setFilter</span><span class="pun">}</span><span class="pln">
  </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">));</span></pre>

<p>
	كما يجب الآن تعديل الملف FilterButton.js لاستخدام الخاصيات التي قدمناها له بالطريقة نفسها التي طبّقناها سابقًا مع المكوِّن <code>&lt;Todo /‎&gt;</code>، لذا طبّق ما يلي وتذكَّر استخدام الأقواس المعقوصة لقراءة هذه المتغيرات:
</p>

<ul>
<li>
		ضع <code>all</code> مكان الخاصية <code>{props.name}</code>.
	</li>
	<li>
		اضبط قيمة السمة <code>aria-pressed</code> على الخاصية <code>{props.isPressed}</code>.
	</li>
	<li>
		أضف السمة <code>onClick</code> التي تستدعي الخطّاف <code>props.setFilter()‎</code> مع اسم المرشِّح.
	</li>
</ul>
<p>
	يجب أن تكون الآن الدالة <code>FilterButton()‎</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_51" style="">
<span class="kwd">function</span><span class="pln"> </span><span class="typ">FilterButton</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">button
      type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln">
      className</span><span class="pun">=</span><span class="str">"btn toggle-btn"</span><span class="pln">
      aria</span><span class="pun">-</span><span class="pln">pressed</span><span class="pun">={</span><span class="pln">props</span><span class="pun">.</span><span class="pln">isPressed</span><span class="pun">}</span><span class="pln">
      onClick</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> props</span><span class="pun">.</span><span class="pln">setFilter</span><span class="pun">(</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">)}</span><span class="pln">
    </span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="typ">Show</span><span class="pln"> </span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">span</span><span class="pun">&gt;{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="pln"> tasks</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	انتقل إلى متصفحك مرةً أخرى، إذ يجب أن ترى تسمية الأزرار المختلفة بأسمائها الخاصة، فإذا ضغطتَ على زر الترشيح Filter، فيجب أن ترى نَص الزر يأخذ تخطيطًا جديدًا، إذ يخبرك هذا بأنه مُحدَّد، فإذا ألقيت نظرةً على فاحص صفحة أدوات التطوير DevTool’s Page Inspector أثناء النقر على الأزرار، فسترى أنّ قيم السمة <code>aria-pressed</code> تتغير وفقًا لذلك.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98850" href="https://academy.hsoub.com/uploads/monthly_2022_05/02_filter-buttons.png.ab0c0205b129430ec98c855ae37354b6.png" rel=""><img alt="02_filter-buttons.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98850" data-unique="1hsp0yeuy" src="https://academy.hsoub.com/uploads/monthly_2022_05/02_filter-buttons.png.ab0c0205b129430ec98c855ae37354b6.png" style="width: 400px; height: auto;"></a>
</p>

<p>
	لكن لا تزال الأزرار لا ترشِّح المهام في واجهة المستخدِم.
</p>

<h3>
	ترشيح المهام في واجهة المستخدم
</h3>

<p>
	يربط الثابت <code>taskList</code> في الدالة <code>App()‎</code> حاليًا حالة المهام ويعيد مكوِّن <code>&lt;Todo /‎&gt;</code> جديدًا لكل منها، ولكننا لا نريد ذلك، إذ يجب تصيير المهمة فقط إذا كانت مُضمَّنةً في نتائج تطبيق المرشِّح المحدَّد، وبالتالي يجب ترشيح حالة المهام باستخدام التابع <code>Array.prototype.filter()‎</code> قبل ربطها لإزالة الكائنات التي لا نريد تصييرها، لذا عدِّل الثابت <code>taskList</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6324_54" style="">
<span class="kwd">const</span><span class="pln"> taskList </span><span class="pun">=</span><span class="pln"> tasks
</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">FILTER_MAP</span><span class="pun">[</span><span class="pln">filter</span><span class="pun">])</span><span class="pln">
</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">task </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln">
    id</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
    name</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
    completed</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln">
    key</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
    toggleTaskCompleted</span><span class="pun">={</span><span class="pln">toggleTaskCompleted</span><span class="pun">}</span><span class="pln">
    deleteTask</span><span class="pun">={</span><span class="pln">deleteTask</span><span class="pun">}</span><span class="pln">
    editTask</span><span class="pun">={</span><span class="pln">editTask</span><span class="pun">}</span><span class="pln">
  </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">));</span></pre>

<p>
	يمكن الوصول إلى قيمة في المصفوفة <code>FILTER_MAP</code> التي تتوافق مع مفتاح حالة المرشِّح لتحديد دالة رد النداء التي يجب استخدامها في التابع <code>Array.prototype.filter()‎</code>، فإذا كان المرشِّح هو <code>All</code> مثلًا، فسيُقيَّم العنصر <code>FILTER_MAP[filter]‎</code> على <code>‎() =&gt; true</code>، كما يؤدي اختيار المرشِّح في متصفحك الآن إلى إزالة المهام التي لا تفي بمعاييره، في حين سيتغير العدد الموجود في العنوان أعلى القائمة ليمثِّل القائمة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98851" href="https://academy.hsoub.com/uploads/monthly_2022_05/03_filtered-todo-list.png.a2394e8e9d49f8bd03b51f0235b8abf0.png" rel=""><img alt="03_filtered-todo-list.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98851" data-unique="p7t9hijbu" src="https://academy.hsoub.com/uploads/monthly_2022_05/03_filtered-todo-list.thumb.png.7d7016a584eae2c5220b2ae1d0759947.png" style="width: 400px; height: auto;"></a>
</p>

<h2>
	الخلاصة
</h2>

<p>
	اكتمل تطبيقنا الآن، ولكن يمكننا إجراء بعض التحسينات لضمان إمكانية استخدام مجموعة أكبر من المستخدِمين له، كما يتناول المقال التالي تضمين إدارة التركيز Focus Management في React التي يمكنها تحسين قابلية الاستخدام وتقليل الارتباك لكل من مستخدِمي لوحة المفاتيح فقط وقارئات الشاشة.
</p>

<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_filtering_conditional_rendering" rel="external nofollow">React interactivity: Editing, filtering, conditional rendering</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A7%D9%84%D8%AA%D9%81%D8%A7%D8%B9%D9%84-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D9%88%D8%A7%D9%84%D8%AD%D8%A7%D9%84%D8%A9-r1572/" rel="">تنفيذ التفاعل في تطبيق React: الأحداث والحالة </a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%82%D8%A7%D8%A6%D9%85%D8%A9-%D9%85%D9%87%D8%A7%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-react-r1570/" rel="">إنشاء تطبيق قائمة مهام باستخدام React</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%AA%D9%82%D8%B3%D9%8A%D9%85-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-%D8%A5%D9%84%D9%89-%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-r1571/" rel="">تقسيم تطبيق React إلى مكونات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1071/" rel="">أساسيات بناء تطبيقات الويب</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1573</guid><pubDate>Sat, 02 Jul 2022 15:00:05 +0000</pubDate></item><item><title>&#x62A;&#x646;&#x641;&#x64A;&#x630; &#x627;&#x644;&#x62A;&#x641;&#x627;&#x639;&#x644; &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642; React: &#x627;&#x644;&#x623;&#x62D;&#x62F;&#x627;&#x62B; &#x648;&#x627;&#x644;&#x62D;&#x627;&#x644;&#x629;</title><link>https://academy.hsoub.com/programming/javascript/react/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%A7%D9%84%D8%AA%D9%81%D8%A7%D8%B9%D9%84-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D9%88%D8%A7%D9%84%D8%AD%D8%A7%D9%84%D8%A9-r1572/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_05/628247b854034_----React.png.c3b7085c69a519b247505f730c8ba869.png" /></p>

<p>
	حان الوقت الآن لتعديل تطبيقنا من واجهة مستخدِم ثابتة تمامًا إلى واجهة مستخدِم تسمح لنا بالتفاعل معها وتعديلها بعد وضع خطة للمكوّنات من خلال البحث عن الأحداث Events والحالة State، إذ ينتج في النهاية تطبيق يمكننا من خلاله إضافة المهام وحذفها بنجاح، ووضع علامة على المهام المكتملة.
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a> ومعرفة استخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">سطر الأوامر أو الطرفية</a>.
	</li>
	<li>
		<strong>الهدف</strong>: التعرف على كيفية التعامل مع الأحداث والحالة في <a href="https://wiki.hsoub.com/React" rel="external">React</a>، واستخدامها لإنشاء دراسة حالة تطبيق تفاعلي.
	</li>
</ul>
<h2>
	معالجة الأحداث
</h2>

<p>
	إذا استخدَمت لغة جافاسكربت الصرفة Vanilla JavaScript سابقًا، فلا بدّ أنك معتاد على وجود ملف جافاسكربت منفصل، إذ يمكنك الاستعلام عن بعض عُقد <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">DOM</a> وربط المستمعين بها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_8" style="">
<span class="kwd">const</span><span class="pln"> btn </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'button'</span><span class="pun">);</span><span class="pln">

btn</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'click'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">"hi!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يمكننا في <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%A7-%D9%87%D9%8A-react%D8%9F-r773/" rel="">React</a> كتابة معالِجات الأحداث مباشرةً للعناصر الموجودة في JSX كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_10" style="">
<span class="pun">&lt;</span><span class="pln">button
  type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln">
  onClick</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">"hi!"</span><span class="pun">)}</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="typ">Say</span><span class="pln"> hi</span><span class="pun">!</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span></pre>

<p>
	يمكن أن يُعَدّ ذلك أمرًا غير مناسب لنصائح أفضل الممارسات التي تميل إلى عدم استخدام معالِجات الأحداث المُضمَّنة في HTML، ولكن تذكر أنّ صيغة <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B5%D9%8A%D8%BA%D8%A9-javascript-syntax-extension-jsx-r777/" rel="">JSX</a> هي جزء من <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">لغة جافاسكربت</a>.
</p>

<p>
	أضفنا في المثال السابق السمة <code>onClick</code> إلى عنصر الزر <code>&lt;button&gt;</code>، وقيمة هذه السمة هي دالة تشغّل تنبيهًا بسيطًا، إذ تملك السمة <code>onClick</code> معنًى خاصًا هنا، فهي تخبر React بتشغيل دالة معيّنة عندما ينقر المستخدِم على الزر، كما يجب ملاحظة بعض الأشياء الأخرى، وهي:
</p>

<ul>
<li>
		تُعَدّ طبيعة اسم السمة <code>onClick</code> ذات حالة الجَمل Camel-cased مهمةً، إذ لن تتعرف صيغة JSX على السمة <code>onclick</code>، لأن هذه الكلمة محجوزة في لغة جافاسكربت، وتُستخدَم لغرض مختلف يمثل خاصيات معالِج الحدث <code>onclick</code> المعيارية.
	</li>
	<li>
		تتبع جميع أحداث المتصفح هذا التنسيق في صيغة JSX باستخدام الجزء <code>on</code> متبوعًا باسم الحدث.
	</li>
</ul>
<p>
	لنطبّق هذه الملاحظات على تطبيقنا بدءًا من المكوِّن <code>Form.js</code>.
</p>

<h3>
	معالجة إرسال النموذج
</h3>

<p>
	أنشئ دالةً بالاسم <code>handleSubmit()‎</code> في الجزء العلوي من دالة المكوِّن <code>Form()‎</code>، إذ يجب على هذه الدالة منع سلوك الحدث <code>submit</code> الافتراضي، ثم يجب إطلاق تنبيه <code>alert()‎</code> بالذي تريده كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_15" style="">
<span class="kwd">function</span><span class="pln"> handleSubmit</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  e</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">();</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">'Hello, world!'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكنك استخدام هذه الدالة من خلال إضافة السمة <code>onSubmit</code> إلى العنصر <code>&lt;form&gt;</code>، وضبط قيمتها على الدالة <code>handleSubmit</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_17" style="">
<span class="pun">&lt;</span><span class="pln">form onSubmit</span><span class="pun">={</span><span class="pln">handleSubmit</span><span class="pun">}&gt;</span></pre>

<p>
	إذا عدت إلى متصفحك ونقرت على زر "الإضافة Add"، فسيعرض المتصفح مربع حوار تنبيه يحتوي على الرسالة "Hello, world!‎" أو أيّ شيء آخر اخترته.
</p>

<h2>
	خاصيات رد النداء
</h2>

<p>
	يندر اقتصار التفاعل على مكوِّن واحد فقط في تطبيقات React، إذ ستؤثِّر الأحداث التي تحدث في أحد المكوِّنات على أجزاء أخرى من التطبيق، فإذا كان لدينا القدرة على إنشاء مهام جديدة مثلًا، فستؤثِّر الأشياء التي تحدث في المكوِّن <code>&lt;Form /‎&gt;</code> على القائمة المُصيَّرة في المكوِّن <code>&lt;App /‎&gt;</code>.
</p>

<p>
	نريد أن تساعدنا الدالة <code>handleSubmit()‎</code> في إنشاء مهمة جديدة، لذلك سنحتاج إلى طريقة لتمرير المعلومات من المكوِّن <code>&lt;Form /‎&gt;</code> إلى المكوِّن <code>&lt;App /‎&gt;</code>، فلا يمكننا تمرير البيانات من الابن إلى الأب بالطريقة نفسها التي نمرر بها البيانات من الأب إلى الابن باستخدام الخاصيات Props المعيارية، إذ يمكننا بدلًا من ذلك كتابة دالة في المكوِّن <code>&lt;App /‎&gt;</code> تتوقع بعض البيانات من نموذجنا بوصفها دخلًا لها، ثم تمرير هذه الدالة إلى المكوِّن <code>&lt;Form /‎&gt;</code> بوصفها خاصيةً، وتُسمَّى هذه الدالة التي تُعامَل على أنها خاصية بخاصية رد النداء Callback Prop، إذ يمكننا استدعاء خاصية رد النداء ضمن المكوِّن <code>&lt;Form /‎&gt;</code> لإرسال البيانات الصحيحة إلى المكوِّن <code>&lt;App /‎&gt;</code>.
</p>

<h3>
	معالجة إرسال النموذج باستخدام خاصيات رد النداء
</h3>

<p>
	أنشئ دالةً بالاسم <code>addTask()‎</code> في الجزء العلوي من دالة المكوِّن <code>App()‎</code>، بحيث تحتوي هذه الدالة على معامِل واحد هو <code>name</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_19" style="">
<span class="kwd">function</span><span class="pln"> addTask</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنمرِّر بعد ذلك الدالة <code>addTask()‎</code> إلى المكوِّن <code>&lt;Form /‎&gt;</code> بوصفها خاصيةً، إذ يمكن أن تحمل الخاصية أيّ اسم تريده، ولكن اختر اسمًا تفهمه لاحقًا مثل الاسم <code>addTask</code> الذي يتطابق مع اسم الدالة ومع ما ستفعله، وهنا يجب تعديل استدعاء المكوِّن <code>&lt;Form /‎&gt;</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_21" style="">
<span class="pun">&lt;</span><span class="typ">Form</span><span class="pln"> addTask</span><span class="pun">={</span><span class="pln">addTask</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span></pre>

<p>
	أخيرًا، يمكنك استخدام هذه الخاصية ضمن الدالة <code>handleSubmit()‎</code> في المكوِّن <code>&lt;Form /‎&gt;</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_23" style="">
<span class="kwd">function</span><span class="pln"> handleSubmit</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  e</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">();</span><span class="pln">
  props</span><span class="pun">.</span><span class="pln">addTask</span><span class="pun">(</span><span class="str">"Say hello!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيؤدي النقر على زر "الإضافة Add" في متصفحك إلى إثبات عمل دالة رد النداء <code>addTask()‎</code>، لكن سيكون جيدًا أن نحصل على تنبيه لإظهار ما نكتبه في حقل الإدخال، وهذا ما سنفعله لاحقًا.
</p>

<p>
	ملاحظة: سمّينا خاصية رد النداء بالاسم <code>addTask</code> لتسهيل فهم ما ستفعله هذه الخاصية، ومن الاصطلاحات الشائعة الأخرى التي قد تصادفها في شيفرة React هي أن تُسبَق أسماء خاصيات رد النداء بالكلمة <code>on</code> متبوعةً باسم الحدث الذي سيؤدي إلى تشغيلها، إذ يمكننا مثلًا تسمية خاصية بالاسم <code>onSubmit</code> مع القيمة <code>addTask</code>.
</p>

<h2>
	الحالة والخطاف useState
</h2>

<p>
	استخدَمنا حتى الآن الخاصيات لتمرير البيانات عبر المكوّنات، ولكننا نحتاج إلى شيء آخر عند تعاملنا مع دخل المستخدِم وتحديثات البيانات.
</p>

<p>
	تأتي الخاصيات من أب المكوِّن، فلن يرث المكوِّن <code>&lt;Form /‎&gt;</code> مثلًا اسمًا جديدًا للمهمة، إذ يتواجد العنصر <code>&lt;input /‎&gt;</code> مباشرةً ضمن المكوِّن <code>&lt;Form /‎&gt;</code>، لذا سيكون هذا المكوِّن مسؤولًا مباشرةً عن إنشاء هذا الاسم الجديد، كما لا يمكننا الطلب من المكوِّن <code>&lt;Form /‎&gt;</code> إنشاء خاصياته تلقائيًا، ولكن يمكننا أن نطلب منه تتبع بعض بياناته، إذ تسمى هذه البيانات التي يمتلكها المكوِّن نفسه بالحالة State، والتي تُعَدّ أداةً قويةً أخرى من React، لأن المكوّنات لا تمتلك الحالة فحسب، وإنما يمكنها تحديثها لاحقًا، في حين لا يمكن تحديث الخاصيات التي يتلقاها المكوِّن، فهي للقراءة فقط.
</p>

<p>
	توفِّر React مجموعةً متنوعةً من الدوال الخاصة التي تسمح لنا بتوفير إمكانات جديدة للمكوّنات مثل الحالة، إذ تُسمَّى هذه الدوال بالخطّافات Hooks، والخطّاف <code>useState</code> -كما يوحي اسمه- هو بالضبط الذي نحتاجه لإعطاء مكوِّننا حالةً، وهنا يجب استيراد خطّاف React من الوحدة <code>react</code> لاستخدامه، لذا عدِّل السطر الأول في الملف Form.js ليصبح كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_25" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useState </span><span class="pun">}</span><span class="pln"> from </span><span class="str">"react"</span><span class="pun">;</span></pre>

<p>
	يسمح لنا ذلك باستيراد الدالة <code>useState()‎</code>، واستخدامها في أيّ مكان من هذا الملف، كما تنشئ هذه الدالة حالةً لمكوِّن، ويحدِّد معامِلها الوحيد القيمة الأولية لتلك الحالة، كما تُعيد هذه الدالة الحالةَ ودالةً يمكن استخدامها لتحديث الحالة لاحقًا، ولنجرب ذلك أولًا من خلال إنشاء الحالة <code>name</code> ودالةً لتحديث هذه الحالة، لذا اكتب السطر التالي قبل الدالة <code>handleSubmit()‎</code> ضمن الدالة <code>Form()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_27" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> setName</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">'Use hooks!'</span><span class="pun">);</span></pre>

<p>
	يحدُث ما يلي في السطر السابق:
</p>

<ul>
<li>
		ضبط قيمة الحالة <code>name</code> الأولية على القيمة "Use hooks!‎".
	</li>
	<li>
		تعريف دالة بالاسم <code>setName()‎</code> وظيفتها تعديل الحالة <code>name</code>.
	</li>
	<li>
		تعيد الدالة <code>useState()‎</code> الشيئين السابقين، لذا فإننا نستخدِم عملية هدم المصفوفات Array Destructuring لإسنادهما إلى متغيرَين منفصلين.
	</li>
</ul>
<h3>
	حالة القراءة
</h3>

<p>
	يمكنك رؤية الحالة <code>name</code> قيد التشغيل مباشرةً، لذا أضِف السمة <code>value</code> إلى العنصر <code>input</code> في النموذج، واضبط قيمتها لتكون <code>name</code>، إذ سيصيِّر متصفحك التعليمة "Use hooks!‎" في العنصر <code>input</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_29" style="">
<span class="pun">&lt;</span><span class="pln">input
  type</span><span class="pun">=</span><span class="str">"text"</span><span class="pln">
  id</span><span class="pun">=</span><span class="str">"new-todo-input"</span><span class="pln">
  className</span><span class="pun">=</span><span class="str">"input input__lg"</span><span class="pln">
  name</span><span class="pun">=</span><span class="str">"text"</span><span class="pln">
  autoComplete</span><span class="pun">=</span><span class="str">"off"</span><span class="pln">
  value</span><span class="pun">={</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
</span><span class="pun">/&gt;</span></pre>

<p>
	عدِّل بعد ذلك التعليمة "Use hooks!‎" إلى سلسلة نصية فارغة، فهذا ما نريده لحالتنا الأولية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_31" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> setName</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">''</span><span class="pun">);</span></pre>

<h3>
	قراءة دخل المستخدم
</h3>

<p>
	يجب التقاط دخل المستخدِم الذي يكتبه قبل تمكننا من تغيير قيمة الحالة <code>name</code>من خلال الاستماع إلى الحدث <code>onChange</code>، فلنكتب الدالة <code>handleChange()‎</code>، ونستمع إليها على الوسم <code>&lt;input /‎&gt;</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_33" style="">
<span class="com">// قُرب أعلى المكوِّن ‫`Form`</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> handleChange</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Typing!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// بعد تعليمة‫ `return`</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">input
  type</span><span class="pun">=</span><span class="str">"text"</span><span class="pln">
  id</span><span class="pun">=</span><span class="str">"new-todo-input"</span><span class="pln">
  className</span><span class="pun">=</span><span class="str">"input input__lg"</span><span class="pln">
  name</span><span class="pun">=</span><span class="str">"text"</span><span class="pln">
  autoComplete</span><span class="pun">=</span><span class="str">"off"</span><span class="pln">
  value</span><span class="pun">={</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
  onChange</span><span class="pun">={</span><span class="pln">handleChange</span><span class="pun">}</span><span class="pln">
</span><span class="pun">/&gt;</span></pre>

<p>
	لن تتغير قيمة الدخل أثناء الكتابة حاليًا، ولكن سيطبع متصفحك الكلمة "Typing!" على طرفية جافاسكربت (نافذة console)، وبالتالي سنعلم أنّ مستمع الحدث متصل بالدخل، كما يمكنك تغيير قيمة الدخل من خلال استخدام الدالة <code>handleChange()‎</code> لتحديث الحالة <code>name</code>.
</p>

<p>
	يمكن قراءة محتويات حقل الإدخال عند تغييرها من خلال الوصول إلى الخاصية <code>value</code> الخاصة بالعنصر <code>input</code> عن طريق قراءة القيمة <code>e.target.value</code> ضمن الدالة <code>handleChange()‎</code>، إذ يمثِّل <code>e.target</code> العنصر الذي أَطلق الحدث <code>change</code>، وهو العنصر <code>input</code>، وبالتالي تكون الخاصية <code>value</code> هي النص الموجود ضمنها، كما يمكنك تنفيذ التابع <code>console.log()‎</code> على هذه القيمة لرؤيتها في طرفية متصفحك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_35" style="">
<span class="kwd">function</span><span class="pln"> handleChange</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">target</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	حالة التحديث
</h3>

<p>
	يجب تخزين حالة <code>name</code> المحدَّثة مع تغير قيمة الدخل، لذا غيِّر التابع <code>console.log()‎</code> إلى <code>setName()‎</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_37" style="">
<span class="kwd">function</span><span class="pln"> handleChange</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  setName</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">target</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب الآن تعديل الدالة <code>handleSubmit()‎</code> لتستدعي الخاصية <code>props.addTask</code> مع الحالة <code>name</code> على أساس وسيط، مما يؤدي إلى إرسال المهمة مرةً أخرى إلى المكوِّن <code>App</code>، لنتمكن من إضافتها إلى قائمة المهام لاحقًا، كما يجب مسح الدخل بعد إرسال النموذج، لذلك سنستدعي الدالة <code>setName()‎</code> مرةً أخرى مع سلسلة نصية فارغة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_40" style="">
<span class="kwd">function</span><span class="pln"> handleSubmit</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  e</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">();</span><span class="pln">
  props</span><span class="pun">.</span><span class="pln">addTask</span><span class="pun">(</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
  setName</span><span class="pun">(</span><span class="str">""</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أخيرًا، يمكنك كتابة شيء ما في حقل الإدخال في متصفحك والنقر فوق زر "Add"، وبالتالي سيظهر كل ما كتبته في مربع حوار التنبيه، والآن يجب أن يكون الملف Form.js كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_42" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useState </span><span class="pun">}</span><span class="pln"> from </span><span class="str">"react"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> </span><span class="typ">Form</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> setName</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">""</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">function</span><span class="pln"> handleChange</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setName</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">target</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">function</span><span class="pln"> handleSubmit</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    e</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">();</span><span class="pln">
    props</span><span class="pun">.</span><span class="pln">addTask</span><span class="pun">(</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
    setName</span><span class="pun">(</span><span class="str">""</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">form onSubmit</span><span class="pun">={</span><span class="pln">handleSubmit</span><span class="pun">}&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">h2 className</span><span class="pun">=</span><span class="str">"label-wrapper"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">label htmlFor</span><span class="pun">=</span><span class="str">"new-todo-input"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"label__lg"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">What</span><span class="pln"> needs to be done</span><span class="pun">?</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">label</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">input
        type</span><span class="pun">=</span><span class="str">"text"</span><span class="pln">
        id</span><span class="pun">=</span><span class="str">"new-todo-input"</span><span class="pln">
        className</span><span class="pun">=</span><span class="str">"input input__lg"</span><span class="pln">
        name</span><span class="pun">=</span><span class="str">"text"</span><span class="pln">
        autoComplete</span><span class="pun">=</span><span class="str">"off"</span><span class="pln">
        value</span><span class="pun">={</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
        onChange</span><span class="pun">={</span><span class="pln">handleChange</span><span class="pun">}</span><span class="pln">
      </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"submit"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn btn__primary btn__lg"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="typ">Add</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">Form</span><span class="pun">;</span></pre>

<p>
	ملاحظة: هناك شيء واحد ستلاحظه، وهو أنه يمكنك إرسال مهام فارغة عند الضغط على زر الإضافة Add دون إدخال اسم المهمة، لذلك يجب منع إضافة المهام الفارغة من خلال إضافة تحقق ما إلى الدالة <code>handleSubmit()‎</code> على سبيل المثال.
</p>

<h2>
	إضافة مهمة
</h2>

<p>
	أصبحنا الآن جاهزين لكتابة دوال تسمح للمستخدِم بإضافة مهمة جديدة من متصفحه بعد أن تدربنا على الأحداث وخاصيات رد النداء والخطّافات.
</p>

<h3>
	استخدام المهام بوصفها حالات
</h3>

<p>
	استورِد الخطّاف <code>useState</code> إلى الملف App.js لتتمكن من تخزين المهام في حالة ما من خلال تعديل سطر استيراد <code>React</code> إلى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_44" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useState </span><span class="pun">}</span><span class="pln"> from </span><span class="str">"react"</span><span class="pun">;</span></pre>

<p>
	نريد تمرير الخاصية <code>props.tasks</code> إلى الخطّاف <code>useState()‎</code>، إذ ستحتفظ هذه الخاصية بحالة الخطّاف الأولية، لذا أضف السطر التالي في الجزء العلوي من تعريف الدالة <code>App()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_46" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">tasks</span><span class="pun">,</span><span class="pln"> setTasks</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="pln">props</span><span class="pun">.</span><span class="pln">tasks</span><span class="pun">);</span></pre>

<p>
	يمكننا الآن تغيير ربط قائمة المهام <code>taskList</code> لتكون نتيجةً لربط <code>tasks</code> بدلًا من <code>props.tasks</code>، إذ يجب أن يبدو تصريح الثابت <code>taskList</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_48" style="">
<span class="kwd">const</span><span class="pln"> taskList </span><span class="pun">=</span><span class="pln"> tasks</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">task </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln">
        id</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
        name</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
        completed</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln">
        key</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
      </span><span class="pun">/&gt;</span><span class="pln">
    </span><span class="pun">)</span><span class="pln">
  </span><span class="pun">);</span></pre>

<h3>
	إضافة مهمة
</h3>

<p>
	لدينا الآن الخطّاف <code>setTasks</code> الذي يمكننا استخدامه في الدالة <code>addTask()‎</code> لتحديث قائمة المهام، ولكن هناك مشكلة أنه لا يمكننا فقط تمرير الوسيط <code>name</code> الخاص بالدالة <code>addTask()‎</code> إلى الخطاف <code>setTasks</code>، لأنّ <code>tasks</code> هي مصفوفة من الكائنات؛ أما الوسيط <code>name</code>، فهو سلسلة نصية، وبالتالي ستكون السلسلة النصية مكان المصفوفة.
</p>

<p>
	يجب أولًا وضع الوسيط <code>name</code> في كائن له بنية مهامنا الحالية نفسها، إذ سننشئ ضمن الدالة <code>addTask()‎</code> الكائن <code>newTask</code> لإضافته إلى المصفوفة، ويجب بعد ذلك إنشاء مصفوفة جديدة مع إضافة هذه المهمة الجديدة إليها، ثم تحديث حالة بيانات المهام إلى هذه الحالة الجديدة من خلال استخدام صيغة الانتشار Spread Syntax لنسخ المصفوفة الحالية، وإضافة الكائن في النهاية، ثم تمرير هذه المصفوفة إلى الدالة <code>setTasks()‎</code> لتحديث الحالة، وبالتالي يجب أن تصبح الدالة <code>addTask()‎</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_50" style="">
<span class="kwd">function</span><span class="pln"> addTask</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> newTask </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">"id"</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
  setTasks</span><span class="pun">([...</span><span class="pln">tasks</span><span class="pun">,</span><span class="pln"> newTask</span><span class="pun">]);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكنك الآن استخدام المتصفح لإضافة مهمة إلى بياناتنا، لذا اكتب أيّ شيء تريده في النموذج، وانقر زر الإضافة Add أو اضغط على مفتاح <code>Enter</code> من لوحة المفاتيح، إذ سترى عنصر المهام الجديد يظهَر في واجهة المستخدِم، لكن هناك مشكلة أخرى تتمثّل بإعطاء الدالة <code>addTask()‎</code> المعرّف <code>id</code> نفسه لكل مهمَّة، إذ يُعَدّ ذلك أمرًا سيئًا لإمكانية الوصول، كما يجعل التمييز بين المهام المستقبلية أمرًا مستحيلًا على React باستخدام الخاصية <code>key</code>، إذ ستعطيك React تحذيرًا في طرفية أدوات التطوير DevTools مثل رسالة التحذير التالية: "Warning: Encountered two children with the same key…‎".
</p>

<p>
	يجب إصلاح هذه المشكلة، كما يُعَدّ إنشاء معرّفات فريدة مشكلةً صعبةً، وهي مشكلة كتَب لها مجتمع جافاسكربت بعض المكتبات المفيدة من أجلها، إذ سنستخدِم حاليًا المكتبة <a href="https://github.com/ai/nanoid" rel="external nofollow">nanoid</a> لأنها صغيرة الحجم وتعمل جيدًا، لذا تأكّد من أنك في المجلد الجذر لتطبيقك وشغّل الأمر التالي في طرفيتك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_52" style="">
<span class="pln">npm install nanoid</span></pre>

<p>
	وفي ملاحظة مهمة، إذا أردتَ استخدام مدير الحزم yarn، فيجب كتابة الأمر: <code>yarn add nanoid</code>.
</p>

<p>
	يمكننا الآن استيراد المكتبة <code>nanoid</code> في الجزء العلوي من الملف App.js لنتمكّن من استخدامها لإنشاء معرِّفات فريدة لمهامنا الجديدة، لذا ضمّن أولًا سطر الاستيراد التالي في أعلى الملف App.js:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_54" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> nanoid </span><span class="pun">}</span><span class="pln"> from </span><span class="str">"nanoid"</span><span class="pun">;</span></pre>

<p>
	لنحدّث الآن الدالة <code>addTask()‎</code> بحيث يصبح كل معرِّف مهمة مؤلفًا من البادئة <code>todo-‎</code> بالإضافة إلى سلسلة نصية فريدة تنشئها المكتبة nanoid، لذا عدِّل تصريح الثابت <code>newTask</code> إلى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_56" style="">
<span class="kwd">const</span><span class="pln"> newTask </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">"todo-"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> nanoid</span><span class="pun">(),</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">};</span></pre>

<p>
	احفظ كل شيء، وجرب تطبيقك مرةً أخرى، إذ يمكنك الآن إضافة المهام دون تلقِّي هذا التحذير بشأن المعرِّفات المُكرَّرة.
</p>

<h2>
	عد المهام
</h2>

<p>
	يمكننا الآن إضافة مهام جديدة، ولكن هناك مشكلة تتمثَّل بقراءة العنوان ثلاث مهام متبقية، بغض النظر عن عدد المهام، إذ يمكن إصلاح ذلك عن طريق حساب طول قائمة المهام <code>taskList</code> وتغيير نص العنوان وفقًا لذلك، لذا أضف ما يلي ضمن تعريف الدالة <code>App()‎</code> قبل تعليمة <code>return</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_59" style="">
<span class="kwd">const</span><span class="pln"> headingText </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">taskList</span><span class="pun">.</span><span class="pln">length</span><span class="pun">}</span><span class="pln"> tasks remaining</span><span class="pun">`;</span></pre>

<p>
	هذا صحيح تقريبًا باستثناء أنه إذا احتوت قائمتنا على مهمة واحدة، فسيظل العنوان يستخدِم الكلمة "tasks"، ولذلك يجب أن نجعلها متغيرةً، لذا عدِّل الشيفرة التي أضفتها للتو كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_61" style="">
<span class="kwd">const</span><span class="pln"> tasksNoun </span><span class="pun">=</span><span class="pln"> taskList</span><span class="pun">.</span><span class="pln">length </span><span class="pun">!==</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> </span><span class="str">'tasks'</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">'task'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> headingText </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">taskList</span><span class="pun">.</span><span class="pln">length</span><span class="pun">}</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">tasksNoun</span><span class="pun">}</span><span class="pln"> remaining</span><span class="pun">`;</span></pre>

<p>
	يمكنك الآن استبدال المتغير <code>headingText</code> بمحتوى نص عنوان القائمة، لذا عدِّل العنصر <code>&lt;h2&gt;</code> ليصبح كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7665_63" style="">
<span class="tag">&lt;h2</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"list-heading"</span><span class="tag">&gt;</span><span class="pln">{headingText}</span><span class="tag">&lt;/h2&gt;</span></pre>

<h2>
	إكمال مهمة
</h2>

<p>
	لاحظ أنّ مربع الاختيار يُحدَّد ويُلغَى تحديده بطريقة مناسبة عند النقر عليه، كما تُعَدّ معرفة المتصفح لكيفية تذكّر مدخلات مربعات الاختيار المُحدَّدة أو المُلغَى تحديدها دون مساعدتنا ميزةً في لغة HTML، ولكن تخفي هذه الميزة مشكلةً، إذ لا يغيِّر تحديد مربع الاختيار أو عدم تحديده الحالة في تطبيق React، مما يعني عدم تزامن المتصفح مع التطبيق، لذلك يجب كتابة شيفرتنا لإعادة المتصفح متزامنًا مع التطبيق.
</p>

<h3>
	إثبات الخطأ
</h3>

<p>
	لنكتب الدالة <code>toggleTaskCompleted()‎</code> في المكوِّن <code>App()‎</code>، إذ سيكون لهذه الدالة المعامِل <code>id</code>، لكننا لن نستخدِمها حاليًا، إذ سنسجِّل الآن المهمة الأولى في المصفوفة في الطرفية، وسنفحص ما يحدث عندما نحدِّدها أو نلغي تحديدها في متصفحنا، لذا أضف ما يلي قبل التصريح عن الثابت <code>taskList</code> مباشرةً:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_65" style="">
<span class="kwd">function</span><span class="pln"> toggleTaskCompleted</span><span class="pun">(</span><span class="pln">id</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">tasks</span><span class="pun">[</span><span class="lit">0</span><span class="pun">])</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنضيف بعد ذلك الخاصية <code>toggleTaskCompleted</code> إلى خاصيات كل مكوّن من مكوّنات <code>&lt;Todo /‎&gt;</code> المُصيَّرة ضمن <code>taskList</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_67" style="">
<span class="kwd">const</span><span class="pln"> taskList </span><span class="pun">=</span><span class="pln"> tasks</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">task </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln">
      id</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
      name</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
      completed</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln">
      key</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
      toggleTaskCompleted</span><span class="pun">={</span><span class="pln">toggleTaskCompleted</span><span class="pun">}</span><span class="pln">
  </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">));</span></pre>

<p>
	انتقل إلى المكوِّن <code>Todo.js</code> وأضف معالِج الحدث <code>onChange</code> إلى العنصر <code>&lt;input /‎&gt;</code> الذي يجب أن يستخدِم دالةً مجهولةً لاستدعاء الخاصية <code>props.toggleTaskCompleted()‎</code> مع المعامِل <code>props.id</code>، إذ يجب أن يكون العنصر <code>&lt;input /‎&gt;</code> الآن كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_69" style="">
<span class="pun">&lt;</span><span class="pln">input
  id</span><span class="pun">={</span><span class="pln">props</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
  type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pln">
  defaultChecked</span><span class="pun">={</span><span class="pln">props</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln">
  onChange</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> props</span><span class="pun">.</span><span class="pln">toggleTaskCompleted</span><span class="pun">(</span><span class="pln">props</span><span class="pun">.</span><span class="pln">id</span><span class="pun">)}</span><span class="pln">
</span><span class="pun">/&gt;</span></pre>

<p>
	احفظ كل شيء وعُد إلى متصفحك ولاحظ تحديد المهمة الأولى <code>Eat</code>، ثم افتح طرفية جافاسكربت، ثم انقر على مربع الاختيار الموجود بجوار الخيار Eat، وبالتالي فإنّ مربع الاختيار هذا غير محدَّد كما توقّعنا، ولكن ستعطي طرفية جافاسكربت الخاصة بك شيئًا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_72" style="">
<span class="typ">Object</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">"task-0"</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Eat"</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	يُلغَى تحديد مربع الاختيار في المتصفح، لكن تخبرنا الطرفية بأن المهمَّة Eat لا تزال مكتملةً، وسنصلح ذلك لاحقًا.
</p>

<h3>
	مزامنة المتصفح مع بياناتنا
</h3>

<p>
	لنَعُد إلى الدالة <code>toggleTaskCompleted()‎</code> في الملف App.js، إذ نريدها أن تغيِّر الخاصية <code>completed</code> للمهمَّة التي أُلغِي تحديدها فقط، وترك المهام الأخرى كما هي، لذلك سنطبّق التابع <code>map()‎</code> على قائمة المهام وسنغيّر القائمة التي أكملناها فقط، لذا عدِّل الدالة <code>toggleTaskCompleted()‎</code> إلى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_74" style="">
<span class="kwd">function</span><span class="pln"> toggleTaskCompleted</span><span class="pun">(</span><span class="pln">id</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> updatedTasks </span><span class="pun">=</span><span class="pln"> tasks</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">task </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// إذا كان لهذه المهمة معرِّف المهمة المُعدَّلة نفسه</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">id </span><span class="pun">===</span><span class="pln"> task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// استخدم انتشار الكائن لإنشاء كائن جديد</span><span class="pln">
      </span><span class="com">// ‫عُدِّلت الخاصية `completed` الخاصة به</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{...</span><span class="pln">task</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="pun">!</span><span class="pln">task</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> task</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  setTasks</span><span class="pun">(</span><span class="pln">updatedTasks</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عرّفنا الثابت <code>updatedTasks</code> الذي يمر على عناصر المصفوفة <code>tasks</code> الأصلية، فإذا طابقت خاصية معرّفُ <code>id</code> المهمة المعرّفَ <code>id</code> المقدَّم للدالة، فسنستخدِم صيغة انتشار الكائن Object Spread Syntax لإنشاء كائن جديد، وسنبدّل إلى الخاصية <code>checked</code> لهذا الكائن قبل إعادته؛ أما إذا لم يتطابقا، فسنعيد الكائن الأصلي.
</p>

<p>
	نستدعي بعد ذلك الدالة <code>setTasks()‎</code> مع هذه المصفوفة الجديدة لتحديث الحالة.
</p>

<h2>
	حذف مهمة
</h2>

<p>
	سيتَّبع حذف مهمة نمطًا مشابهًا لتبديل حالتها المكتملة، إذ يجب تعريف دالة لتحديث الحالة، ثم تمرير هذه الدالة إلى المكوِّن <code>&lt;Todo /‎&gt;</code> بوصفها خاصيةً، واستدعاؤها عند حدوث الحدث الصحيح.
</p>

<h3>
	خاصية رد النداء deleteTask
</h3>

<p>
	سنكتب الدالة <code>deleteTask()‎</code> في المكوِّن <code>App</code>، إذ ستأخذ هذه الدالة المعامِل <code>id</code> مثل الدالة <code>toggleTaskCompleted()‎</code>، وسنسجّل هذا المعرِّف في الطرفية، لذا أضف ما يلي بعد الدالة <code>toggleTaskCompleted()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_76" style="">
<span class="kwd">function</span><span class="pln"> deleteTask</span><span class="pun">(</span><span class="pln">id</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">id</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أضف بعد ذلك خاصية رد نداء أخرى إلى مصفوفة مكوّنات <code>&lt;Todo /‎&gt;</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_78" style="">
<span class="kwd">const</span><span class="pln"> taskList </span><span class="pun">=</span><span class="pln"> tasks</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">task </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln">
    id</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
    name</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
    completed</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln">
    key</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
    toggleTaskCompleted</span><span class="pun">={</span><span class="pln">toggleTaskCompleted</span><span class="pun">}</span><span class="pln">
    deleteTask</span><span class="pun">={</span><span class="pln">deleteTask</span><span class="pun">}</span><span class="pln">
  </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">));</span></pre>

<p>
	يجب استدعاء الدالة <code>props.deleteTask()‎</code> في الملف Todo.js عند الضغط على زر الحذف Delete، كما تحتاج الدالة <code>deleteTask()‎</code> إلى معرفة معرِّف المهمَّة التي ستستدعيها لتتمكّن من حذف المهمَّة الصحيحة من الحالة، لذا عدِّل زر الحذف Delete ضمن الملف Todo.js كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_80" style="">
<span class="pun">&lt;</span><span class="pln">button
  type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln">
  className</span><span class="pun">=</span><span class="str">"btn btn__danger"</span><span class="pln">
  onClick</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> props</span><span class="pun">.</span><span class="pln">deleteTask</span><span class="pun">(</span><span class="pln">props</span><span class="pun">.</span><span class="pln">id</span><span class="pun">)}</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="typ">Delete</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span></pre>

<p>
	إذا نقرتَ الآن على أيّ من أزرار الحذف Delete في التطبيق، فيجب أن تسجِّل طرفية المتصفح معرّف المهمَّة المرتبطة به.
</p>

<h2>
	حذف المهام من الحالة وواجهة المستخدم
</h2>

<p>
	يمكننا الآن استدعاء الخطّاف <code>setTasks()‎</code> في الدالة <code>deleteTask()‎</code> استدعاءً صحيحًا لحذف هذه المهمة فعليًا من حالة التطبيق وحذفها مرئيًا من واجهة مستخدِم التطبيق، وبما أنّ الخطّاف <code>setTasks()‎</code> يتوقع مصفوفةً بوصفها وسيطًا له، فيجب تزويده بمصفوفة جديدة تنسخ المهام الحالية باستثناء المهمة التي يتطابق معرِّفها مع معرِّف المهمَّة المُمرَّرة إلى الدالة <code>deleteTask()‎</code>.
</p>

<p>
	يمكننا الآن استخدام التابع <code>Array.prototype.filter()‎</code>، إذ يمكننا اختبار كل مهمة، واستبعاد مهمة من المصفوفة الجديدة إذا تطابقت خاصيتها <code>id</code> مع المعامِل <code>id</code> المُمرَّر إلى الدالة <code>deleteTask()‎</code>، لذا عدِّل الدالة <code>deleteTask()‎</code> ضمن الملف App.js كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7665_82" style="">
<span class="kwd">function</span><span class="pln"> deleteTask</span><span class="pun">(</span><span class="pln">id</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> remainingTasks </span><span class="pun">=</span><span class="pln"> tasks</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">task </span><span class="pun">=&gt;</span><span class="pln"> id </span><span class="pun">!==</span><span class="pln"> task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">);</span><span class="pln">
  setTasks</span><span class="pun">(</span><span class="pln">remainingTasks</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	جرِّب تطبيقك مرةً أخرى، إذ يجب أن تكون قادرًا على حذف مهمَّة من تطبيقك.
</p>

<h2>
	الخلاصة
</h2>

<p>
	قدّمنا في هذا المقال معلومات حول كيفية تعامل React مع الأحداث والحالة، وتنفيذ وظائف إضافة المهام وحذفها ووضع علامة على المهام المكتملة، كما سنطبّق في المقال التالي وظيفة تعديل المهام الحالية وترشيح قائمة المهام جميعها والمهام المكتملة وغير المكتملة فقط، كما سنطّلع على التصيير الشرطي <a href="https://academy.hsoub.com/design/user-interface/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-ui-r652/" rel="">لواجهة المستخدِم UI</a>.
</p>

<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_events_state" rel="external nofollow">React interactivity: Events and state</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%82%D8%A7%D8%A6%D9%85%D8%A9-%D9%85%D9%87%D8%A7%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-react-r1570/" rel="">إنشاء تطبيق قائمة مهام باستخدام React</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%AA%D9%82%D8%B3%D9%8A%D9%85-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-%D8%A5%D9%84%D9%89-%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-r1571/" rel="">تقسيم تطبيق React إلى مكونات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1071/" rel="">أساسيات بناء تطبيقات الويب</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1572</guid><pubDate>Wed, 29 Jun 2022 15:04:01 +0000</pubDate></item><item><title>&#x62A;&#x642;&#x633;&#x64A;&#x645; &#x62A;&#x637;&#x628;&#x64A;&#x642; React &#x625;&#x644;&#x649; &#x645;&#x643;&#x648;&#x646;&#x627;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/react/%D8%AA%D9%82%D8%B3%D9%8A%D9%85-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-%D8%A5%D9%84%D9%89-%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-r1571/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_05/628243248ef4c_--React--.png.24dc5cacbaf93f9d2544b5051b2f9912.png" /></p>

<p>
	يُعَدّ تطبيقنا الذي عملنا عليه في <a href="https://academy.hsoub.com/programming/javascript/react/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%82%D8%A7%D8%A6%D9%85%D8%A9-%D9%85%D9%87%D8%A7%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-react-r1570/" rel="">المقال السابق</a> وحدةً متراصةً، لذلك يجب تقسيمه إلى مكوّنات يمكن وصفها وإدارتها قبل تمكننا من جعل تطبيقنا يفعل شيئًا ما، إذ لا تحتوي مكتبة React على قواعد صارمة لتحديد ما يُعَدّ مكوّنًا Component، فالأمر متروك لك، وسنعرض في هذا المقال طريقةً معقولةً لتقسيم تطبيقنا إلى مكونات.
</p>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a> ومعرفة استخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">سطر الأوامر أو الطرفية</a>.
	</li>
	<li>
		<strong>الهدف</strong>: إظهار طريقة لتقسيم تطبيق قائمة المهام إلى مكوّنات.
	</li>
</ul>
<h2>
	تحديد المكون الأول
</h2>

<p>
	قد يبدو تحديد أحد المكوّنات أمرًا صعبًا إلى حين حصولك على بعض الخبرة العملية، ولكن الأمور المهمة هي:
</p>

<ul>
<li>
		إذا كان أحد الأشياء جزءًا واضحًا من تطبيقك، فيُحتمَل أن يكون مكوّنًا.
	</li>
	<li>
		إذا أُعيد استخدام أحد الأشياء كثيرًا، فيُحتمَل أن يكون مكوّنًا.
	</li>
</ul>
<p>
	تُعَدّ النقطة الثانية ذات قيمة خاصة، إذ يتيح إنشاء مكوّن من عناصر واجهة المستخدِم تعديلَ شيفرتك البرمجية في مكان واحد ورؤية تلك التعديلات في جميع الأماكن التي يُستخدَم فيها هذا المكوّن، إذ ليس عليك تقسيم كل شيء إلى مكوِنات مباشرةً، ولنستخدِم النقطة الثانية لإنشاء مكوّن من أكثر الأجزاء المُعاد استخدامها والأكثر أهميةً في واجهة المستخدِم وهو عنصر قائمة المهام.
</p>

<h2>
	إنشاء المكون &lt;Todo /‎&gt;<todo></todo>
</h2>

<p>
	يجب علينا إنشاء ملف جديد للمكوّن قبل إنشائه، ويجب إنشاء مجلد لهذه المكونات، إذ تنشئ الأوامر التالية المجلد components وملفًا ضمنه يُسمَّى Todo.js، ولكن تأكّد من وجودك في جذر تطبيقك قبل تشغيل هذه الأوامر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_9" style="">
<span class="pln">mkdir src</span><span class="pun">/</span><span class="pln">components
touch src</span><span class="pun">/</span><span class="pln">components</span><span class="pun">/</span><span class="typ">Todo</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	إنّ ملف Todo.js الجديد فارغ حاليًا، لذا افتحه واكتب فيه السطر الأول التالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_1317_11" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">"react"</span><span class="pun">;</span></pre>

<p>
	سننشئ مكوّنًا يسمّى <code>Todo</code>، لذلك يمكننا إضافة شيفرتنا إلى الملف Todo.js على النحو التالي، إذ سنعرِّف الدالة <code>Todo()‎</code> ونصدّرها على السطر نفسه كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_13" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="typ">Todo</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">

  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	كل شيء جيد حتى الآن، لكن يجب أن يعيد المكون شيئًا ما، لذا ارجع إلى الملف src/App.js، وانسخ أول عنصر <code>&lt;li&gt;</code> من القائمة غير المرتبة، والصقه في الملف Todo.js بحيث يصبح كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_15" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="typ">Todo</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">li className</span><span class="pun">=</span><span class="str">"todo stack-small"</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"c-cb"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">input id</span><span class="pun">=</span><span class="str">"todo-0"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pln"> defaultChecked</span><span class="pun">={</span><span class="kwd">true</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">label className</span><span class="pun">=</span><span class="str">"todo-label"</span><span class="pln"> htmlFor</span><span class="pun">=</span><span class="str">"todo-0"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">Eat</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">label</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"btn-group"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">Edit</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="typ">Eat</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn btn__danger"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">Delete</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="typ">Eat</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">li</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ملاحظة: يجب أن تعيد المكونات شيئًا ما دائمًا، فإذا حاولت لاحقًا تصيير Render مكون لا يعيد شيئًا، فستعرِض <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%A7-%D9%87%D9%8A-react%D8%9F-r773/" rel="">React</a> خطأً في متصفحك.
</p>

<p>
	أصبح المكوّن <code>Todo</code> مكتملًا حاليًا، وبالتالي يمكننا استخدامه، والآن أضف السطر التالي في الملف App.js بالقرب من أعلى الملف لاستيراد المكوّن <code>Todo</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_18" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">Todo</span><span class="pln"> from </span><span class="str">"./components/Todo"</span><span class="pun">;</span></pre>

<p>
	يمكنك مع استيراد هذا المكون وضع استدعاءات المكوّن <code>&lt;Todo /‎&gt;</code> مكان جميع عناصر <code>&lt;li&gt;</code> في الملف App.js، ويجب أن يصبح العنصر <code>&lt;ul&gt;</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_20" style="">
<span class="pun">&lt;</span><span class="pln">ul
  role</span><span class="pun">=</span><span class="str">"list"</span><span class="pln">
  className</span><span class="pun">=</span><span class="str">"todo-list stack-large stack-exception"</span><span class="pln">
  aria</span><span class="pun">-</span><span class="pln">labelledby</span><span class="pun">=</span><span class="str">"list-heading"</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">ul</span><span class="pun">&gt;</span></pre>

<p>
	إذا نظرت إلى متصفحك، فستلاحظ شيئًا مؤسفًا، إذ تُكرِّر قائمتك المهمة الأولى ثلاث مرات كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98845" href="https://academy.hsoub.com/uploads/monthly_2022_05/01_todo-list-repeating-todos.png.6b83a96723188682831d916a9e9287ce.png" rel=""><img alt="01_todo-list-repeating-todos.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98845" data-unique="0r2jg5u8s" src="https://academy.hsoub.com/uploads/monthly_2022_05/01_todo-list-repeating-todos.thumb.png.14713e151d2ff7a993f79fbcae3e2119.png" style="width: 400px; height: auto;"></a>
</p>

<p>
	لا نريد الأكل فقط، إذ لدينا مهام أخرى يجب تنفيذها، وسنوضّح فيما يلي كيفية إجراء استدعاءات لمكوّنات مختلفة تصيّر محتوًى فريدًا.
</p>

<h2>
	إنشاء مكون &lt;Todo /‎&gt;<todo> فريد</todo>
</h2>

<p>
	تُعَدّ المكونات مهمةً لأنها تتيح إعادة استخدام أجزاء من واجهة المستخدِم، والإشارة إلى مكان ما ليكون مصدرًا لواجهة المستخدِم تلك، ولكن تكمن المشكلة في أننا لا نريد إعادة استخدام جميع المكونات، وإنما نريد إعادة استخدام معظم الأجزاء، وتعديل أجزاء صغيرة، لذا يجب استخدام الخاصيات Props.
</p>

<h3>
	الخاصية name
</h3>

<p>
	إذا أردنا تتبّع أسماء المهام التي نريد إكمالها، فيجب علينا التأكد من أنّ كل مكوّن <code>&lt;Todo /‎&gt;</code> يصيِّر اسمًا فريدًا، لذا امنح كل مكوّن <code>&lt;Todo /‎&gt;</code> في الملف App.js خاصية الاسم <code>name</code>، ولنستخدم أسماء المهام التي كانت لدينا سابقًا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_24" style="">
<span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"Eat"</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"Sleep"</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"Repeat"</span><span class="pln"> </span><span class="pun">/&gt;</span></pre>

<p>
	إذا حدّثتَ متصفحك، فسترى الشيء السابق نفسه بالضبط، إذ أعطينا المكوّن <code>&lt;Todo /‎&gt;</code> بعض الخاصيات، لكننا لم نستخدِمها بعد، فلنَعُد الآن إلى الملف Todo.js ونصلح كل شيء.
</p>

<p>
	عدّل أولًا تعريف الدالة <code>Todo()‎</code> بحيث تأخذ الخاصيات <code>props</code> على أساس معامِل، كما يمكنك تطبيق التابع <code>console.log()‎</code> على الخاصيات <code>props</code> كما فعلنا سابقًا إذا أردت التحقق من استلام المكوّن للخاصيات استلامًا صحيحًا، ثم يمكنك وضع خاصية الاسم <code>name</code> مكان تكرارات المهمة <code>Eat</code>، وتذكّر استخدام الأقواس المعقوصة لحقن قيمة متغير في تعابير <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B5%D9%8A%D8%BA%D8%A9-javascript-syntax-extension-jsx-r777/" rel="">JSX</a>، إذ يجب أن تكون الدالة <code>Todo()‎</code> بعد ذلك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_27" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="typ">Todo</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">li className</span><span class="pun">=</span><span class="str">"todo stack-small"</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"c-cb"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">input id</span><span class="pun">=</span><span class="str">"todo-0"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pln"> defaultChecked</span><span class="pun">={</span><span class="kwd">true</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">label className</span><span class="pun">=</span><span class="str">"todo-label"</span><span class="pln"> htmlFor</span><span class="pun">=</span><span class="str">"todo-0"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">label</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"btn-group"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">Edit</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn btn__danger"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">Delete</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">li</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب أن يعرض متصفحك الآن ثلاث مهام فريدة، ولكن لا تزال جميع هذه المهام مُحدَّدةً افتراضيًا.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98846" href="https://academy.hsoub.com/uploads/monthly_2022_05/02_todo-list-unique-todos.png.d7f9fa0d3440057e25a31c9ab4c0a43f.png" rel=""><img alt="02_todo-list-unique-todos.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98846" data-unique="zpdnk2ssx" src="https://academy.hsoub.com/uploads/monthly_2022_05/02_todo-list-unique-todos.thumb.png.3ec46381d5b2522b8bbfdd27643deb5a.png" style="width: 400px; height: auto;"></a>
</p>

<h3>
	الخاصية completed
</h3>

<p>
	حُدِّدت المهمة <code>Eat</code> فقط في القائمة الثابتة الأصلية، إذ نريد إعادة استخدام معظم واجهة المستخدِم التي تشكل المكوِّن <code>&lt;Todo /‎‎‎&gt;</code> مع تعديل شيء واحد من خلال منح كل استدعاء للمكوّن <code>&lt;Todo /‎‎‎&gt;</code> في الملف App.js الخاصية <code>completed</code> الجديدة، كما يجب أن يكون للخاصية <code>completed</code> التابعة للمكوّن الأول الذي اسمه <code>Eat</code> القيمة <code>true</code>، وللمكونات الأخرى القيمة <code>false</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_30" style="">
<span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"Eat"</span><span class="pln"> completed</span><span class="pun">={</span><span class="kwd">true</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"Sleep"</span><span class="pln"> completed</span><span class="pun">={</span><span class="kwd">false</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"Repeat"</span><span class="pln"> completed</span><span class="pun">={</span><span class="kwd">false</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span></pre>

<p>
	يجب علينا العودة إلى الملف Todo.js لاستخدام هذه الخاصيات، لذا عدّل السمة <code>defaultChecked</code> للعنصر <code>&lt;input /‎&gt;</code> بحيث تساوي قيمتها الخاصية <code>completed</code>، ثم يكون عنصر <code>&lt;input /‎&gt;</code> الخاص بالمكون <code>Todo</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_32" style="">
<span class="pun">&lt;</span><span class="pln">input id</span><span class="pun">=</span><span class="str">"todo-0"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pln"> defaultChecked</span><span class="pun">={</span><span class="pln">props</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span></pre>

<p>
	حدّث متصفحك لإظهار تحديد المكوِّن <code>Eat</code> فقط كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98847" href="https://academy.hsoub.com/uploads/monthly_2022_05/03_todo-list-differing-checked-states.png.098b17e4b4dc018c7367deade512cd03.png" rel=""><img alt="03_todo-list-differing-checked-states.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98847" data-unique="q2vn1cdoe" src="https://academy.hsoub.com/uploads/monthly_2022_05/03_todo-list-differing-checked-states.thumb.png.6612dfe3487031765281fd5a084236a6.png" style="width: 400px; height: auto;"></a>
</p>

<p>
	إذا عدّلت كل خاصيات <code>completed</code> الخاصة بالمكوّن <code>&lt;Todo /‎&gt;</code>، فسيحدِّد متصفحك أو يلغي تحديد مربعات الاختيار المكافئة والمُصيَّرة وفقًا لذلك.
</p>

<h3>
	الخاصية id
</h3>

<p>
	يعطي المكوّن <code>&lt;Todo /‎&gt;</code> لكل مهمة السمة <code>id</code> بالقيمة <code>todo-0</code>، وهذا خطأ في HTML لأن سمات <code>id</code> يجب أن تكون فريدةً، إذ تستخدِمها لغات <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">جافاسكربت</a> و<a href="https://academy.hsoub.com/programming/css/%d8%aa%d8%b9%d8%b1%d9%91%d9%81-%d8%b9%d9%84%d9%89-%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d8%a7%d8%aa-css-r70/" rel="">CSS</a> وغيرها بوصفها معرّفات فريدةً لأجزاء المستند، وبالتالي يجب أن نعطي المكون الخاصية <code>id</code> التي تأخذ قيمةً فريدةً لكل مكوّن <code>Todo</code>.
</p>

<p>
	إذًا لنمنح كل نسخة من المكوِّن <code>&lt;Todo /‎&gt;</code> معرّفًا باستخدام التنسيق <code>todo-i</code>، إذ تزيد قيمة <code>i</code> بمقدار واحد في كل مرة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_37" style="">
<span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"Eat"</span><span class="pln"> completed</span><span class="pun">={</span><span class="kwd">true</span><span class="pun">}</span><span class="pln"> id</span><span class="pun">=</span><span class="str">"todo-0"</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"Sleep"</span><span class="pln"> completed</span><span class="pun">={</span><span class="kwd">false</span><span class="pun">}</span><span class="pln"> id</span><span class="pun">=</span><span class="str">"todo-1"</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"Repeat"</span><span class="pln"> completed</span><span class="pun">={</span><span class="kwd">false</span><span class="pun">}</span><span class="pln"> id</span><span class="pun">=</span><span class="str">"todo-2"</span><span class="pln"> </span><span class="pun">/&gt;</span></pre>

<p>
	عُد الآن إلى الملف Todo.js واستفد من الخاصية <code>id</code>، إذ يجب تعديل قيمة السمة <code>id</code> للعنصر <code>&lt;input /‎&gt;</code> وقيمة السمة <code>htmlFor</code> الخاصة بالعنصر <code>label</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_39" style="">
<span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"c-cb"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">input id</span><span class="pun">={</span><span class="pln">props</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pln"> defaultChecked</span><span class="pun">={</span><span class="pln">props</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">label className</span><span class="pun">=</span><span class="str">"todo-label"</span><span class="pln"> htmlFor</span><span class="pun">={</span><span class="pln">props</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}&gt;</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">props</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">label</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span></pre>

<p>
	كل شيء جيد حتى الآن، ولكن تُعَدّ شيفرتنا مكرَّرةً، فالأسطر الثلاثة التي تصيّر المكوّن <code>&lt;Todo /‎&gt;</code> متطابقة تقريبًا مع اختلاف واحد فقط هو قيمة كل خاصية، كما يمكننا تنظيف شيفرتنا باستخدام إحدى ميزات جافاسكربت الأساسية وهي التكرار Iteration، ولكن يجب أولًا إعادة التفكير في المهام لاستخدام هذه الميزة.
</p>

<h2>
	بيانات المهام
</h2>

<p>
	تحتوي كل مهمة من مهامنا حاليًا على ثلاثة أجزاء من المعلومات، وهي اسمها، وما إذا كانت مُحدَّدة، ومعرّفها الفريد، كما تُترجَم هذه البيانات إلى كائن Object، وبما أنه لدينا أكثر من مهمة، فسنستخدِم مصفوفةً من الكائنات لتمثيل هذه البيانات، لذا أنشئ ثابتًا <code>const</code> جديدًا بعد تعليمة الاستيراد الأخيرة في الملف src/index.js وقبل التابع <code>ReactDOM.render()‎</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_41" style="">
<span class="kwd">const</span><span class="pln"> DATA </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">"todo-0"</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Eat"</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">"todo-1"</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Sleep"</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">"todo-2"</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Repeat"</span><span class="pun">,</span><span class="pln"> completed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">];</span></pre>

<p>
	سنمرِّر بعد ذلك الثابت <code>DATA</code> إلى المكوّن <code>&lt;App /‎&gt;</code> بوصفه خاصيةً تُسمَّى <code>tasks</code>، إذ يجب أن يكون السطر الأخير من الملف src/index.js كما يلي:
</p>

<pre class="ipsCode">
ReactDOM.render(&lt;App tasks={DATA} /&gt;, document.getElementById("root"));
</pre>

<p>
	أصبحت هذه المصفوفة متاحةً الآن للمكون <code>App</code> بالصورة <code>props.tasks</code>، كما يمكنك استخدام التابع <code>console.log()‎</code> للتحقق من ذلك.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		ملاحظة: ليس لأسماء الثوابت ذات الأحرف الكبيرة <code>ALL_CAPS</code> معنًى خاصًا في جافاسكربت، وإنما هي اتفاقية بين المطوِّرين تخبرهم بعدم تغيّر هذه البيانات بعد تعريفها أبدًا.
	</p>
</blockquote>

<h2>
	التصيير مع التكرار
</h2>

<p>
	يمكننا تصيير مصفوفة الكائنات من خلال تحويل كل منها إلى المكون <code>&lt;Todo /‎&gt;</code>، إذ تمنحنا لغة جافاسكربت تابع مصفوفة لتحويل البيانات إلى شيء آخر، وهو <code>Array.prototype.map()‎</code>، لذا أنشئ ثابتًا <code>const</code> جديدًا يسمى <code>taskList</code> قبل تعليمة <code>return</code> الخاصة بالدالة <code>App()‎</code>، واستخدِم التابع <code>map()‎</code> لتحويله، ولنحوّل مجموعة مهامنا إلى شيء بسيط يتمثّل باسم <code>name</code> كل مهمة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_43" style="">
<span class="kwd">const</span><span class="pln"> taskList </span><span class="pun">=</span><span class="pln"> props</span><span class="pun">.</span><span class="pln">tasks</span><span class="pun">?.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">task </span><span class="pun">=&gt;</span><span class="pln"> task</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span></pre>

<p>
	لنحاول وضع الثابت <code>taskList</code> مكان جميع أبناء العنصر <code>&lt;ul&gt;</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_45" style="">
<span class="pun">&lt;</span><span class="pln">ul
  role</span><span class="pun">=</span><span class="str">"list"</span><span class="pln">
  className</span><span class="pun">=</span><span class="str">"todo-list stack-large stack-exception"</span><span class="pln">
  aria</span><span class="pun">-</span><span class="pln">labelledby</span><span class="pun">=</span><span class="str">"list-heading"</span><span class="pln">
</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">taskList</span><span class="pun">}</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">ul</span><span class="pun">&gt;</span></pre>

<p>
	يمنحنا ذلك طريقةً لإظهار جميع المكوّنات مرةً أخرى، إذ يصيّر المتصفح حاليًا اسم كل مهمة بوصفه نصًا دون بنية معينة، كما ينقصنا حاليًا بنية HTML مثل عنصر <code>&lt;li&gt;</code> ومربعات الاختيار والأزرار.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98848" href="https://academy.hsoub.com/uploads/monthly_2022_05/04_todo-list-unstructured-names.png.89986ed90caefd17de42f61dda5539f2.png" rel=""><img alt="04_todo-list-unstructured-names.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98848" data-unique="vs42pmqtc" src="https://academy.hsoub.com/uploads/monthly_2022_05/04_todo-list-unstructured-names.thumb.png.94ef077bc1ef383cb45f6d92bbd456da.png" style="width: 400px; height: auto;"></a>
</p>

<p>
	يمكننا إصلاح ذلك من خلال إعادة المكوّن <code>&lt;Todo /‎&gt;</code> من التابع <code>map()‎</code>، وتذكَّر أنّ صيغة JSX تسمح لنا بخلط بنى جافاسكربت مع اللغات التوصيفية Markup، فلنجرب ما يلي بدلًا مما لدينا حاليًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_49" style="">
<span class="kwd">const</span><span class="pln"> taskList </span><span class="pun">=</span><span class="pln"> props</span><span class="pun">.</span><span class="pln">tasks</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">task </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> </span><span class="pun">/&gt;);</span></pre>

<p>
	انظر مرةً أخرى إلى تطبيقك، إذ تبدو مهامنا الآن كما كانت سابقًا، لكنها تفتقد إلى أسماء المهام نفسها، وتذكَّر أنّ كل مهمة نطبّق عليها التابع <code>map()‎</code> لها الخاصيات <code>id</code> و<code>name</code> و<code>checked</code>، والتي نريد تمريرها إلى المكوّن <code>&lt;Todo /‎&gt;</code>، وبالتالي سنحصل على الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_51" style="">
<span class="kwd">const</span><span class="pln"> taskList </span><span class="pun">=</span><span class="pln"> props</span><span class="pun">.</span><span class="pln">tasks</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">task </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln"> id</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln"> name</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln"> completed</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">));</span></pre>

<p>
	يبدو التطبيق الآن كما كان سابقًا، ولكن أصبحت شيفرتنا أقل تكرارًا.
</p>

<h2>
	خاصيات key الفريدة
</h2>

<p>
	يجب أن تتعقّب React المهام لتصييرها بصورة صحيحة بعد أن صيّرت هذه المهام من مصفوفة، إذ تستخدِم React التخمين لتتبع الأشياء، ولكن يمكننا مساعدتها عن طريق تمرير الخاصية <code>key</code> لمكونات <code>&lt;Todo /‎&gt;</code>، إذ تُعَدّ <code>key</code> خاصيةً خاصةً تديرها React، ولا يمكنك استخدام الكلمة <code>key</code> لأيّ غرض آخر، وبما أنّ الخاصية <code>key</code> يجب أن تكون فريدةً، فسنعيد استخدام خاصية <code>id</code> الخاصة بكل كائن مهمة على أساس مفتاح له، لذا عدِّل الثابت <code>taskList</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_53" style="">
<span class="kwd">const</span><span class="pln"> taskList </span><span class="pun">=</span><span class="pln"> props</span><span class="pun">.</span><span class="pln">tasks</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">task </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln">
      id</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
      name</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
      completed</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln">
      key</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
    </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	يجب عليك دائمًا تمرير مفتاح فريد لأيّ شيء تُصيّره مع التكرار، ولن يتغير شيء واضح في متصفحك، ولكن إذا لم تستخدِم مفاتيح فريدةً، فستعطي React تحذيرات في الطرفية console ويمكن أن يتصرف تطبيقك بطريقة غريبة.
</p>

<h2>
	تقسيم أجزاء التطبيق المتبقية إلى مكونات
</h2>

<p>
	يمكننا الآن تحويل باقي التطبيق إلى مكونات بعد فرزِنا المكوّن الأكثر أهميةً، وتذكَّر أنّ المكونات هي إما أجزاء واضحة من واجهة المستخدِم، أو أجزاء معاد استخدامها من واجهة المستخدِم، أو كليهما، كما يمكننا إنشاء مكونين آخرين هما:
</p>

<ul>
<li>
		<code>&lt;Form/‎&gt;</code>
	</li>
	<li>
		<code>&lt;FilterButton/‎&gt;</code>
	</li>
</ul>
<p>
	بما أننا نعلم بحاجتنا لهذين المكوِنين، فيمكننا تجميع أوامر إنشاء الملفات في أمر واحد في الطرفية، لذا شغّل الأمر التالي في طرفيتك، مع الانتباه إلى أنك في المجلد الجذر لتطبيقك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_55" style="">
<span class="pln">touch src</span><span class="pun">/</span><span class="pln">components</span><span class="pun">/</span><span class="typ">Form</span><span class="pun">.</span><span class="pln">js src</span><span class="pun">/</span><span class="pln">components</span><span class="pun">/</span><span class="typ">FilterButton</span><span class="pun">.</span><span class="pln">js</span></pre>

<h3>
	المكون &lt;Form/‎&gt;
</h3>

<p>
	افتح الملف components/Form.js ونفِّذ ما يلي:
</p>

<ul>
<li>
		استورد مكتبة <code>React</code> في أعلى الملف كما فعلنا في الملف Todo.js.
	</li>
	<li>
		أنشئ المكوِّن <code>Form()‎</code> الجديد باستخدام بنية <code>Todo()‎</code> الأساسية نفسها، ثم صدِّر هذا المكوِّن.
	</li>
	<li>
		انسخ وسوم <code>&lt;form&gt;</code> وما يوجد بينها من الملف App.js، والصقها ضمن تعليمة <code>return</code> الخاصة بالمكوِّن <code>Form()‎</code>.
	</li>
	<li>
		صدّر المكوِّن <code>Form</code> في نهاية الملف.
	</li>
</ul>
<p>
	يجب أن يكون الملف Form.js الآن كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_57" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">"react"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> </span><span class="typ">Form</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">form</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">h2 className</span><span class="pun">=</span><span class="str">"label-wrapper"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">label htmlFor</span><span class="pun">=</span><span class="str">"new-todo-input"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"label__lg"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">What</span><span class="pln"> needs to be done</span><span class="pun">?</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">label</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">input
        type</span><span class="pun">=</span><span class="str">"text"</span><span class="pln">
        id</span><span class="pun">=</span><span class="str">"new-todo-input"</span><span class="pln">
        className</span><span class="pun">=</span><span class="str">"input input__lg"</span><span class="pln">
        name</span><span class="pun">=</span><span class="str">"text"</span><span class="pln">
        autoComplete</span><span class="pun">=</span><span class="str">"off"</span><span class="pln">
      </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"submit"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn btn__primary btn__lg"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="typ">Add</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">Form</span><span class="pun">;</span></pre>

<h3>
	المكون &lt;FilterButton/‎&gt;<filterbutton></filterbutton>
</h3>

<p>
	كرِّر الأمور نفسها التي نفّذتها لإنشاء الملف Form.js على الملف FilterButton.js، ولكن استدعِ المكوِّن <code>FilterButton()‎</code> وانسخ جزء HTML للزر الأول الموجود ضمن العنصر <code>&lt;div&gt;</code> ذو الصنف <code>filters</code> من الملف App.js في تعليمة <code>return</code>، إذ يجب أن يكون الملف الآن كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_59" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">"react"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> </span><span class="typ">FilterButton</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn toggle-btn"</span><span class="pln"> aria</span><span class="pun">-</span><span class="pln">pressed</span><span class="pun">=</span><span class="str">"true"</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="typ">Show</span><span class="pln"> </span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">all </span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="pln"> tasks</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">FilterButton</span><span class="pun">;</span></pre>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		ملاحظة: لاحظ أننا ارتكبنا الخطأ نفسه هنا الذي ارتكبناه لأول مرة مع المكوِّن <code>&lt;Todo /‎&gt;</code>، إذ سيبقى كل زر كما هو، ولكن سنصلح هذا المكوِّن لاحقًا.
	</p>
</blockquote>

<h2>
	استيراد جميع المكونات
</h2>

<p>
	أضف بعض تعليمات الاستيراد <code>import</code> في الجزء العلوي من الملف App.js لاستيراد هذه المكوّنات الجديدة، ثم عدّل تعليمة <code>return</code> الخاصة بالمكوِّن <code>App()‎</code> لتصيير المكونات، إذ يجب أن يكون الملف ‎‎App.js كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1317_62" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">"react"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Form</span><span class="pln"> from </span><span class="str">"./components/Form"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">FilterButton</span><span class="pln"> from </span><span class="str">"./components/FilterButton"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Todo</span><span class="pln"> from </span><span class="str">"./components/Todo"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> </span><span class="typ">App</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> taskList </span><span class="pun">=</span><span class="pln"> props</span><span class="pun">.</span><span class="pln">tasks</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">task </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">Todo</span><span class="pln">
        id</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
        name</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
        completed</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">completed</span><span class="pun">}</span><span class="pln">
        key</span><span class="pun">={</span><span class="pln">task</span><span class="pun">.</span><span class="pln">id</span><span class="pun">}</span><span class="pln">
      </span><span class="pun">/&gt;</span><span class="pln">
    </span><span class="pun">)</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"todoapp stack-large"</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="typ">TodoMatic</span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">Form</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"filters btn-group stack-exception"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">FilterButton</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">FilterButton</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">FilterButton</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">h2 id</span><span class="pun">=</span><span class="str">"list-heading"</span><span class="pun">&gt;</span><span class="lit">3</span><span class="pln"> tasks remaining</span><span class="pun">&lt;/</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">ul
        role</span><span class="pun">=</span><span class="str">"list"</span><span class="pln">
        className</span><span class="pun">=</span><span class="str">"todo-list stack-large stack-exception"</span><span class="pln">
        aria</span><span class="pun">-</span><span class="pln">labelledby</span><span class="pun">=</span><span class="str">"list-heading"</span><span class="pln">
      </span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">taskList</span><span class="pun">}</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">ul</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span><span class="pun">;</span></pre>

<p>
	نكون بذلك جاهزين تقريبًا للتعامل مع التفاعل في تطبيق React الخاص بنا.
</p>

<h2>
	الخلاصة
</h2>

<p>
	تعمّقنا في كيفية تقسيم التطبيق إلى مكوّنات وتصييرها بكفاءة، إذ سننتقل الآن إلى إلقاء نظرة على كيفية التعامل مع الأحداث في React وإضافة التفاعل.
</p>

<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components" rel="external nofollow">Componentizing our React app</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1071/" rel="">أساسيات بناء تطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-react-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-react-components-r834/" rel="">مكونات React الأساسية (React Components)</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1571</guid><pubDate>Sun, 26 Jun 2022 15:00:02 +0000</pubDate></item><item><title>&#x625;&#x646;&#x634;&#x627;&#x621; &#x62A;&#x637;&#x628;&#x64A;&#x642; &#x642;&#x627;&#x626;&#x645;&#x629; &#x645;&#x647;&#x627;&#x645; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; React</title><link>https://academy.hsoub.com/programming/javascript/react/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%82%D8%A7%D8%A6%D9%85%D8%A9-%D9%85%D9%87%D8%A7%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-react-r1570/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_05/62823eb0ec1d6_React.png.bc753b6842be631850fe2f259c545d96.png" /></p>

<p>
	لنفترض أننا نريد توضيح مفهوم <a href="https://wiki.hsoub.com/React" rel="external">React</a> من خلال إنشاء تطبيق يسمح للمستخدِمين بإضافة المهام التي يريدون العمل عليها وتعديلها وحذفها، وكذلك وضع علامة على المهام المكتملة دون حذفها، إذ سنوجّهك من خلال هذا المقال لوضع بنية المكوّن <code>App</code> الأساسية وتصميمه في المكان الصحيح، وتعريف المكوّنات الفردية والتفاعلية التي سنضيفها لاحقًا.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			ملاحظة: إذا أردت التحقق من شيفرتك، فيمكنك العثور على إصدار نهائي من نموذج شيفرتنا لتطبيق React في المستودع <a href="https://github.com/mdn/todo-react" rel="external nofollow">todo-react</a>، وراجع <a href="https://mdn.github.io/todo-react-build/" rel="external nofollow">هذا الرابط</a> للحصول على إصدار حي ومباشر.
		</p>
	</div>
</blockquote>

<ul>
<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a> ومعرفة استخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">سطر الأوامر أو الطرفية</a>.
	</li>
	<li>
		<strong>الهدف</strong>: تقديم دراسة حالة تطبيق قائمة المهام، ووضع بنية المكوّن <code>App</code> الأساسية وتصميمه في المكان الصحيح.
	</li>
</ul>
<h2>
	قصص مستخدم التطبيق
</h2>

<p>
	تُعَدّ قصة المستخدِم في تطوير البرمجيات هدفًا قابلًا للتنفيذ من منظور المستخدِم، إذ سيساعدنا تحديد قصص المستخدِمين قبل البدء في تركيز عملنا، ويجب على تطبيقنا تحقيق القصص التالية، إذ يمكن للمستخِدم تنفيذ ما يلي:
</p>

<ul>
<li>
		قراءة قائمة المهام.
	</li>
	<li>
		إضافة مهمة باستخدام الفأرة أو لوحة المفاتيح.
	</li>
	<li>
		وضْع علامة على المهام المكتملة باستخدام الفأرة أو لوحة المفاتيح.
	</li>
	<li>
		حذف أيّ مهمة باستخدام الفأرة أو لوحة المفاتيح.
	</li>
	<li>
		تعديل أيّ مهمة باستخدام الفأرة أو لوحة المفاتيح.
	</li>
	<li>
		عرض مجموعة فرعية محدَّدة من المهام: جميع المهام، أو المهمة النشطة فقط، أو المهام المكتملة فقط.
	</li>
</ul>
<h2>
	تجهيز المشروع الأولي
</h2>

<p>
	أنشأتْ الأداة <code>create-react-app</code> بعض الملفات التي لن نستخدِمها مطلقًا في مشروعنا، إذ لن نضيف ملف تنسيق سابق لعرض المكونات، لذا احذف أولًا استيراد App.css من أعلى الملف App.js، كما أننا لن نستخِدم الملف logo.svg، لذا أزِل استيراده أيضًا، ثم انسخ والصق بعد ذلك الأوامر التالية في طرفيتك لحذف بعض الملفات غير الضرورية، وتأكّد من أنك تبدأ من المجلد الجذر للتطبيق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8943_7" style="">
<span class="pun">#</span><span class="pln"> </span><span class="pun">‫انتقل</span><span class="pln"> </span><span class="pun">إلى</span><span class="pln"> </span><span class="pun">المجلد</span><span class="pln"> src </span><span class="pun">الخاص</span><span class="pln"> </span><span class="pun">بمشروعك</span><span class="pln">
cd src
</span><span class="pun">#</span><span class="pln"> </span><span class="pun">احذف</span><span class="pln"> </span><span class="pun">بعض</span><span class="pln"> </span><span class="pun">الملفات</span><span class="pln">
rm </span><span class="pun">--</span><span class="pln"> </span><span class="typ">App</span><span class="pun">.</span><span class="pln">test</span><span class="pun">.</span><span class="pln">js </span><span class="typ">App</span><span class="pun">.</span><span class="pln">css logo</span><span class="pun">.</span><span class="pln">svg serviceWorker</span><span class="pun">.</span><span class="pln">js setupTests</span><span class="pun">.</span><span class="pln">js
</span><span class="pun">#</span><span class="pln"> </span><span class="pun">انتقل</span><span class="pln"> </span><span class="pun">احتياطيًا</span><span class="pln"> </span><span class="pun">إلى</span><span class="pln"> </span><span class="pun">جذر</span><span class="pln"> </span><span class="pun">المشروع</span><span class="pln">
cd </span><span class="pun">..</span></pre>

<p>
	ملاحظتان:
</p>

<ul>
<li>
		هناك ملفان من الملفات التي حذفناها مخصَّصان لاختبار التطبيق، وبالتالي لن نغطّي الاختبار في مثالنا.
	</li>
	<li>
		إذا أوقفت خادمك لتنفيذ المهام السابقة في الطرفية، فيجب تشغيله مرةً أخرى باستخدام الأمر <code>npm start</code>.
	</li>
</ul>
<h2>
	شيفرة المشروع الأساسية
</h2>

<p>
	سنقدِّم فيما يلي شيفرة الدالة <code>App()‎</code> وشيفرة <a href="https://academy.hsoub.com/programming/css/%d8%aa%d8%b9%d8%b1%d9%91%d9%81-%d8%b9%d9%84%d9%89-%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d8%a7%d8%aa-css-r70/" rel="">CSS</a> لتنسيق تطبيقك لتستخدمها بدلًا من الشيفرة التي لديك الآن.
</p>

<h3>
	صيغة JSX
</h3>

<p>
	انسخ مقتطف الشيفرة التالي إلى مفكرتك، ثم الصقه في الملف App.js بحيث يحل محل دالة <code>App()‎</code> الحالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8943_10" style="">
<span class="kwd">function</span><span class="pln"> </span><span class="typ">App</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"todoapp stack-large"</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="typ">TodoMatic</span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">form</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">h2 className</span><span class="pun">=</span><span class="str">"label-wrapper"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">label htmlFor</span><span class="pun">=</span><span class="str">"new-todo-input"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"label__lg"</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="typ">What</span><span class="pln"> needs to be done</span><span class="pun">?</span><span class="pln">
          </span><span class="pun">&lt;/</span><span class="pln">label</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">input
          type</span><span class="pun">=</span><span class="str">"text"</span><span class="pln">
          id</span><span class="pun">=</span><span class="str">"new-todo-input"</span><span class="pln">
          className</span><span class="pun">=</span><span class="str">"input input__lg"</span><span class="pln">
          name</span><span class="pun">=</span><span class="str">"text"</span><span class="pln">
          autoComplete</span><span class="pun">=</span><span class="str">"off"</span><span class="pln">
        </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"submit"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn btn__primary btn__lg"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">Add</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"filters btn-group stack-exception"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn toggle-btn"</span><span class="pln"> aria</span><span class="pun">-</span><span class="pln">pressed</span><span class="pun">=</span><span class="str">"true"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="typ">Show</span><span class="pln"> </span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">all</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="pln"> tasks</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn toggle-btn"</span><span class="pln"> aria</span><span class="pun">-</span><span class="pln">pressed</span><span class="pun">=</span><span class="str">"false"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="typ">Show</span><span class="pln"> </span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">span</span><span class="pun">&gt;</span><span class="typ">Active</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="pln"> tasks</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn toggle-btn"</span><span class="pln"> aria</span><span class="pun">-</span><span class="pln">pressed</span><span class="pun">=</span><span class="str">"false"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="typ">Show</span><span class="pln"> </span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">span</span><span class="pun">&gt;</span><span class="typ">Completed</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="pln"> tasks</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">h2 id</span><span class="pun">=</span><span class="str">"list-heading"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="lit">3</span><span class="pln"> tasks remaining
      </span><span class="pun">&lt;/</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">ul
        role</span><span class="pun">=</span><span class="str">"list"</span><span class="pln">
        className</span><span class="pun">=</span><span class="str">"todo-list stack-large stack-exception"</span><span class="pln">
        aria</span><span class="pun">-</span><span class="pln">labelledby</span><span class="pun">=</span><span class="str">"list-heading"</span><span class="pln">
      </span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">li className</span><span class="pun">=</span><span class="str">"todo stack-small"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"c-cb"</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">input id</span><span class="pun">=</span><span class="str">"todo-0"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pln"> defaultChecked</span><span class="pun">={</span><span class="kwd">true</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">label className</span><span class="pun">=</span><span class="str">"todo-label"</span><span class="pln"> htmlFor</span><span class="pun">=</span><span class="str">"todo-0"</span><span class="pun">&gt;</span><span class="pln">
              </span><span class="typ">Eat</span><span class="pln">
            </span><span class="pun">&lt;/</span><span class="pln">label</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"btn-group"</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn"</span><span class="pun">&gt;</span><span class="pln">
              </span><span class="typ">Edit</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="typ">Eat</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn btn__danger"</span><span class="pun">&gt;</span><span class="pln">
              </span><span class="typ">Delete</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="typ">Eat</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">li</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">li className</span><span class="pun">=</span><span class="str">"todo stack-small"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"c-cb"</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">input id</span><span class="pun">=</span><span class="str">"todo-1"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">label className</span><span class="pun">=</span><span class="str">"todo-label"</span><span class="pln"> htmlFor</span><span class="pun">=</span><span class="str">"todo-1"</span><span class="pun">&gt;</span><span class="pln">
              </span><span class="typ">Sleep</span><span class="pln">
            </span><span class="pun">&lt;/</span><span class="pln">label</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"btn-group"</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn"</span><span class="pun">&gt;</span><span class="pln">
              </span><span class="typ">Edit</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="typ">Sleep</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn btn__danger"</span><span class="pun">&gt;</span><span class="pln">
              </span><span class="typ">Delete</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="typ">Sleep</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">li</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">li className</span><span class="pun">=</span><span class="str">"todo stack-small"</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"c-cb"</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">input id</span><span class="pun">=</span><span class="str">"todo-2"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">label className</span><span class="pun">=</span><span class="str">"todo-label"</span><span class="pln"> htmlFor</span><span class="pun">=</span><span class="str">"todo-2"</span><span class="pun">&gt;</span><span class="pln">
              </span><span class="typ">Repeat</span><span class="pln">
            </span><span class="pun">&lt;/</span><span class="pln">label</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"btn-group"</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn"</span><span class="pun">&gt;</span><span class="pln">
              </span><span class="typ">Edit</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="typ">Repeat</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn btn__danger"</span><span class="pun">&gt;</span><span class="pln">
              </span><span class="typ">Delete</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="typ">Repeat</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">li</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">ul</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	افتح الآن الملف public/index.html وعدّل نص العنصر <code>&lt;title&gt;</code> ليصبح <code>TodoMatic</code>، بحيث يطابق العنصر <code>&lt;h1&gt;</code> الموجود أعلى تطبيقنا.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8943_14" style="">
<span class="tag">&lt;title&gt;</span><span class="pln">TodoMatic</span><span class="tag">&lt;/title&gt;</span></pre>

<p>
	يجب أن ترى ما يلي عند تحديث متصفحك:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="98841" href="https://academy.hsoub.com/uploads/monthly_2022_05/01_unstyled-app.png.707acf3659b0047aa80460406eebb826.png" rel=""><img alt="01_unstyled-app.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98841" data-unique="x2szvgrtl" src="https://academy.hsoub.com/uploads/monthly_2022_05/01_unstyled-app.thumb.png.e4198a05bbc9e1431023262786cc1365.png" style="width: 400px; height: auto;"></a>
</p>

<p>
	لا يبدو هذا التطبيق جميلًا ولا يعمل بعد، لكنه جيد حاليًا، وضَع في بالك <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B5%D9%8A%D8%BA%D8%A9-javascript-syntax-extension-jsx-r777/" rel="">شيفرة JSX</a>، وكيفية توافقها مع قصص المستخدِمين:
</p>

<ul>
<li>
		لدينا عنصر <code>&lt;form&gt;</code> مع العنصر <code>&lt;input type="text"‎&gt;</code> لكتابة مهمة جديدة، وزر لإرسال النموذج.
	</li>
	<li>
		لدينا مجموعة من الأزرار التي سنستخدِمها لمهامنا.
	</li>
	<li>
		لدينا عنوان <code>heading</code> يخبرنا عن عدد المهام المتبقية.
	</li>
	<li>
		لدينا ثلاث مهام مرتبة ضمن قائمة غير مرتبة، إذ تُعَدّ كل مهمة أنها عنصر قائمة <code>&lt;li&gt;</code>، كما تحتوي على أزرار لتعديلها وحذفها، بالإضافة إلى مربع اختيار لإيقاف تشغيلها.
	</li>
</ul>
<p>
	سيسمح النموذج بإنشاء المهام، إذ تسمح الأزرار بترشيح المهام، ويُعَدّ العنوان والقائمة طريقةً لقراءتها، إذ ليست <a href="https://academy.hsoub.com/design/user-interface/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-ui-r652/" rel="">واجهة المستخدِم</a> المُستخدَمة لتعديل مهمة موجودةً في الوقت الحالي، ولكن سنكتبها لاحقًا.
</p>

<h3>
	ميزات الشمولية
</h3>

<p>
	قد تلاحظ بعض السمات غير العادية هنا مثل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8943_19" style="">
<span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"btn toggle-btn"</span><span class="pln"> aria</span><span class="pun">-</span><span class="pln">pressed</span><span class="pun">=</span><span class="str">"true"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="typ">Show</span><span class="pln"> </span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">all</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">span className</span><span class="pun">=</span><span class="str">"visually-hidden"</span><span class="pun">&gt;</span><span class="pln"> tasks</span><span class="pun">&lt;/</span><span class="pln">span</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span></pre>

<p>
	تخبر السمة <code>aria-pressed</code> التقنيات المساعدة مثل قارئات الشاشة أنه قد يكون الزر في إحدى حالتين وهما مضغوط <code>pressed</code> أو غير مضغوط <code>unpressed</code>، إذ تمثِّلان حالة التشغيل <code>on</code> والإيقاف <code>off</code>، ويعني تعيين القيمة <code>true</code> أنّ الزر مضغوط افتراضيًا.
</p>

<p>
	ليس للصنف <code>visually-hidden</code> أيَّ تأثير حتى الآن، لأننا لم نضمِّن شيفرة CSS، فإذا وضعنا التنسيقات في مكانها الصحيح، فسيُخفَى أيّ عنصر في هذا الصنف عن المستخدِمين المبصرين، وسيظل متاحًا لمستخدِمي قارئ الشاشة، لأن هذه الكلمات لا يحتاجها المستخدِمون المبصرون، إذ تُستخدَم لتقديم المزيد من المعلومات حول ما يفعله الزر لمستخدِمي قارئ الشاشة الذين ليس لديهم القدرة البصرية الإضافية لمساعدتهم، كما يمكنك العثور على العنصر <code>&lt;ul&gt;</code> التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8943_21" style="">
<span class="pun">&lt;</span><span class="pln">ul
  role</span><span class="pun">=</span><span class="str">"list"</span><span class="pln">
  className</span><span class="pun">=</span><span class="str">"todo-list stack-large stack-exception"</span><span class="pln">
  aria</span><span class="pun">-</span><span class="pln">labelledby</span><span class="pun">=</span><span class="str">"list-heading"</span><span class="pln">
</span><span class="pun">&gt;</span></pre>

<p>
	تساعد السمة <code>role</code> التقنيات المساعِدة في توضيح نوع العنصر الذي يمثِّله الوسم، إذ يجري التعامل مع العنصر <code>&lt;ul&gt;</code> بوصفه قائمةً افتراضيًا، ولكن ستؤدي التنسيقات التي نضيفها إلى تعطيل هذه الوظيفة، في حين ستؤدي السمة <code>role</code> إلى استعادة القائمة التي تعني العنصر <code>&lt;ul&gt;</code>.
</p>

<p>
	تخبر السمة <code>aria-labelledby</code> التقنيات المساعِدة بتعاملنا مع عنوان قائمتنا بوصفه العنوان الذي يصف الغرض من القائمة الموجودة تحته، مما يساعد مستخدِمي قارئ الشاشة على فهم الغرض منها بصورة أفضل، وأخيرًا، فتملك عناصر <code>label</code> و<code>input</code> في عناصر القائمة بعض السمات الفريدة الخاصة بصيغة JSX، وهي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8943_23" style="">
<span class="pun">&lt;</span><span class="pln">input id</span><span class="pun">=</span><span class="str">"todo-0"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pln"> defaultChecked</span><span class="pun">={</span><span class="kwd">true</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">label className</span><span class="pun">=</span><span class="str">"todo-label"</span><span class="pln"> htmlFor</span><span class="pun">=</span><span class="str">"todo-0"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="typ">Eat</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">label</span><span class="pun">&gt;</span></pre>

<p>
	السمة <code>defaultChecked</code> في الوسم <code>&lt;input /‎&gt;</code> تخبر React بتحديد مربع الاختيار مبدئيًا، فإذا أردنا استخدام السمة <code>checked</code> كما نفعل في HTML، فستعرِض React بعض التحذيرات المتعلقة بمعالجة أحداث مربع الاختيار في طرفية متصفحك (نافذة console)، والتي يجب تجنبها، ولا تقلق كثيرًا بشأن ذلك في الوقت الحالي، إذ سنغطي ذلك لاحقًا عندما نبدأ باستخدام الأحداث.
</p>

<p>
	تتوافق السمة <code>htmlFor</code> مع السمة <code>for</code> المُستخدَمة في لغة HTML، ولا يمكننا استخدام الكلمة <code>for</code> بوصفها سمةً في صيغة JSX لأنها كلمة محجوزة، لذلك تستخدِم React السمة <code>htmlFor</code> بدلًا من ذلك.
</p>

<p>
	ملاحظتان:
</p>

<ul>
<li>
		يمكنك استخدام القيم المنطقية -أي <code>true</code> و<code>false</code>- في سمات JSX من خلال إحاطة هذه القيم بأقواس معقوصة، فإذا كتبت السمة <code>defaultChecked="true"‎</code> مثلًا، فستكون <code>"true"</code> هي قيمة السمة <code>defaultChecked</code>، والتي تُعَدّ سلسلةً حرفيةً String Literal، لأنها لغة جافاسكربت وليست لغة HTML.
	</li>
	<li>
		تملك السمة <code>aria-pressed</code> القيمة <code>"true"</code> في مثالنا لأنّ <code>aria-pressed</code> ليست سمةً منطقيةً حقيقيةً بالطريقة التي تستخدِمها السمة <code>checked</code> بها.
	</li>
</ul>
<h3>
	تنفيذ التنسيقات
</h3>

<p>
	الصق شيفرة CSS التالية في الملف src/index.css لتحُل محل ما هو موجود حاليًا:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_8943_25" style="">
<span class="com">/* إعادة الضبط */</span><span class="pln">
</span><span class="pun">*,</span><span class="pln">
</span><span class="pun">*::</span><span class="pln">before</span><span class="pun">,</span><span class="pln">
</span><span class="pun">*::</span><span class="pln">after </span><span class="pun">{</span><span class="pln">
  box</span><span class="pun">-</span><span class="pln">sizing</span><span class="pun">:</span><span class="pln"> border</span><span class="pun">-</span><span class="pln">box</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">*:</span><span class="pln">focus </span><span class="pun">{</span><span class="pln">
  outline</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3px</span><span class="pln"> dashed </span><span class="com">#228bec;</span><span class="pln">
  outline</span><span class="pun">-</span><span class="pln">offset</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
html </span><span class="pun">{</span><span class="pln">
  font</span><span class="pun">:</span><span class="pln"> </span><span class="lit">62.5</span><span class="pun">%</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">1.15</span><span class="pln"> sans</span><span class="pun">-</span><span class="pln">serif</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
h1</span><span class="pun">,</span><span class="pln">
h2 </span><span class="pun">{</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
ul </span><span class="pun">{</span><span class="pln">
  list</span><span class="pun">-</span><span class="pln">style</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln">
  padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
button </span><span class="pun">{</span><span class="pln">
  border</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln">
  margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  width</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">auto</span><span class="pun">;</span><span class="pln">
  overflow</span><span class="pun">:</span><span class="pln"> visible</span><span class="pun">;</span><span class="pln">
  background</span><span class="pun">:</span><span class="pln"> transparent</span><span class="pun">;</span><span class="pln">
  color</span><span class="pun">:</span><span class="pln"> inherit</span><span class="pun">;</span><span class="pln">
  font</span><span class="pun">:</span><span class="pln"> inherit</span><span class="pun">;</span><span class="pln">
  line</span><span class="pun">-</span><span class="pln">height</span><span class="pun">:</span><span class="pln"> normal</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">-</span><span class="pln">webkit</span><span class="pun">-</span><span class="pln">font</span><span class="pun">-</span><span class="pln">smoothing</span><span class="pun">:</span><span class="pln"> inherit</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">-</span><span class="pln">moz</span><span class="pun">-</span><span class="pln">osx</span><span class="pun">-</span><span class="pln">font</span><span class="pun">-</span><span class="pln">smoothing</span><span class="pun">:</span><span class="pln"> inherit</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">-</span><span class="pln">webkit</span><span class="pun">-</span><span class="pln">appearance</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
button</span><span class="pun">::-</span><span class="pln">moz</span><span class="pun">-</span><span class="pln">focus</span><span class="pun">-</span><span class="pln">inner </span><span class="pun">{</span><span class="pln">
  border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
button</span><span class="pun">,</span><span class="pln">
input</span><span class="pun">,</span><span class="pln">
optgroup</span><span class="pun">,</span><span class="pln">
</span><span class="kwd">select</span><span class="pun">,</span><span class="pln">
textarea </span><span class="pun">{</span><span class="pln">
  font</span><span class="pun">-</span><span class="pln">family</span><span class="pun">:</span><span class="pln"> inherit</span><span class="pun">;</span><span class="pln">
  font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
  line</span><span class="pun">-</span><span class="pln">height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.15</span><span class="pun">;</span><span class="pln">
  margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
button</span><span class="pun">,</span><span class="pln">
input </span><span class="pun">{</span><span class="pln">
  overflow</span><span class="pun">:</span><span class="pln"> visible</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
input</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"text"</span><span class="pun">]</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  border</span><span class="pun">-</span><span class="pln">radius</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
body </span><span class="pun">{</span><span class="pln">
  width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
  max</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">68rem</span><span class="pun">;</span><span class="pln">
  margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="kwd">auto</span><span class="pun">;</span><span class="pln">
  font</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.6rem</span><span class="pun">/</span><span class="lit">1.25</span><span class="pln"> </span><span class="typ">Arial</span><span class="pun">,</span><span class="pln"> sans</span><span class="pun">-</span><span class="pln">serif</span><span class="pun">;</span><span class="pln">
  background</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#f5f5f5;</span><span class="pln">
  color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#4d4d4d;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="lit">@media</span><span class="pln"> screen </span><span class="kwd">and</span><span class="pln"> </span><span class="pun">(</span><span class="pln">min</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">620px</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  body </span><span class="pun">{</span><span class="pln">
    font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.9rem</span><span class="pun">;</span><span class="pln">
    line</span><span class="pun">-</span><span class="pln">height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.31579</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">/* نهاية إعادة الضبط */</span><span class="pln">
</span><span class="com">/* التنسيقات العامة */</span><span class="pln">
</span><span class="pun">.</span><span class="pln">form</span><span class="pun">-</span><span class="kwd">group</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> input</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"text"</span><span class="pun">]</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  display</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">inline</span><span class="pun">-</span><span class="pln">block</span><span class="pun">;</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.4rem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">btn </span><span class="pun">{</span><span class="pln">
  padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.8rem</span><span class="pln"> </span><span class="lit">1rem</span><span class="pln"> </span><span class="lit">0.7rem</span><span class="pun">;</span><span class="pln">
  border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.2rem</span><span class="pln"> solid </span><span class="com">#4d4d4d;</span><span class="pln">
  cursor</span><span class="pun">:</span><span class="pln"> pointer</span><span class="pun">;</span><span class="pln">
  text</span><span class="pun">-</span><span class="pln">transform</span><span class="pun">:</span><span class="pln"> capitalize</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">btn</span><span class="pun">.</span><span class="pln">toggle</span><span class="pun">-</span><span class="pln">btn </span><span class="pun">{</span><span class="pln">
  border</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pun">;</span><span class="pln">
  border</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#d3d3d3;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">btn</span><span class="pun">.</span><span class="pln">toggle</span><span class="pun">-</span><span class="pln">btn</span><span class="pun">[</span><span class="pln">aria</span><span class="pun">-</span><span class="pln">pressed</span><span class="pun">=</span><span class="str">"true"</span><span class="pun">]</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  text</span><span class="pun">-</span><span class="pln">decoration</span><span class="pun">:</span><span class="pln"> underline</span><span class="pun">;</span><span class="pln">
  border</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#4d4d4d;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">btn__danger </span><span class="pun">{</span><span class="pln">
  color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#fff;</span><span class="pln">
  background</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#ca3c3c;</span><span class="pln">
  border</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#bd2130;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">btn__filter </span><span class="pun">{</span><span class="pln">
  border</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> lightgrey</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">btn__primary </span><span class="pun">{</span><span class="pln">
  color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#fff;</span><span class="pln">
  background</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#000;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">btn</span><span class="pun">-</span><span class="kwd">group</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  display</span><span class="pun">:</span><span class="pln"> flex</span><span class="pun">;</span><span class="pln">
  justify</span><span class="pun">-</span><span class="pln">content</span><span class="pun">:</span><span class="pln"> space</span><span class="pun">-</span><span class="pln">between</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">btn</span><span class="pun">-</span><span class="kwd">group</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  flex</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="lit">49</span><span class="pun">%;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">btn</span><span class="pun">-</span><span class="kwd">group</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.8rem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">label</span><span class="pun">-</span><span class="pln">wrapper </span><span class="pun">{</span><span class="pln">
  margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  flex</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
  text</span><span class="pun">-</span><span class="pln">align</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">visually</span><span class="pun">-</span><span class="pln">hidden </span><span class="pun">{</span><span class="pln">
  position</span><span class="pun">:</span><span class="pln"> absolute </span><span class="pun">!</span><span class="pln">important</span><span class="pun">;</span><span class="pln">
  height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pun">;</span><span class="pln">
  width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pun">;</span><span class="pln">
  overflow</span><span class="pun">:</span><span class="pln"> hidden</span><span class="pun">;</span><span class="pln">
  clip</span><span class="pun">:</span><span class="pln"> rect</span><span class="pun">(</span><span class="lit">1px</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> </span><span class="lit">1px</span><span class="pun">);</span><span class="pln">
  clip</span><span class="pun">:</span><span class="pln"> rect</span><span class="pun">(</span><span class="lit">1px</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1px</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1px</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1px</span><span class="pun">);</span><span class="pln">
  white</span><span class="pun">-</span><span class="pln">space</span><span class="pun">:</span><span class="pln"> nowrap</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">[</span><span class="kwd">class</span><span class="pun">*=</span><span class="str">"stack"</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">stack</span><span class="pun">-</span><span class="pln">small </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.25rem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">stack</span><span class="pun">-</span><span class="pln">large </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2.5rem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="lit">@media</span><span class="pln"> screen </span><span class="kwd">and</span><span class="pln"> </span><span class="pun">(</span><span class="pln">min</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">550px</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">stack</span><span class="pun">-</span><span class="pln">small </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    margin</span><span class="pun">-</span><span class="pln">top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.4rem</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">stack</span><span class="pun">-</span><span class="pln">large </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    margin</span><span class="pun">-</span><span class="pln">top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2.8rem</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">stack</span><span class="pun">-</span><span class="pln">exception </span><span class="pun">{</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.2rem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">/* نهاية التنسيقات العامة */</span><span class="pln">
</span><span class="pun">.</span><span class="pln">todoapp </span><span class="pun">{</span><span class="pln">
  background</span><span class="pun">:</span><span class="pln"> </span><span class="com">#fff;</span><span class="pln">
  margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2rem</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">4rem</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1rem</span><span class="pun">;</span><span class="pln">
  position</span><span class="pun">:</span><span class="pln"> relative</span><span class="pun">;</span><span class="pln">
  box</span><span class="pun">-</span><span class="pln">shadow</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> </span><span class="lit">4px</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> rgba</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.2</span><span class="pun">),</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">2.5rem</span><span class="pln"> </span><span class="lit">5rem</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> rgba</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.1</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="lit">@media</span><span class="pln"> screen </span><span class="kwd">and</span><span class="pln"> </span><span class="pun">(</span><span class="pln">min</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">550px</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">todoapp </span><span class="pun">{</span><span class="pln">
    padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4rem</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">todoapp </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  max</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">50rem</span><span class="pun">;</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">left</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">auto</span><span class="pun">;</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">right</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">auto</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">todoapp </span><span class="pun">&gt;</span><span class="pln"> form </span><span class="pun">{</span><span class="pln">
  max</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">todoapp </span><span class="pun">&gt;</span><span class="pln"> h1 </span><span class="pun">{</span><span class="pln">
  display</span><span class="pun">:</span><span class="pln"> block</span><span class="pun">;</span><span class="pln">
  max</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
  text</span><span class="pun">-</span><span class="pln">align</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
  margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1rem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">label__lg </span><span class="pun">{</span><span class="pln">
  line</span><span class="pun">-</span><span class="pln">height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.01567</span><span class="pun">;</span><span class="pln">
  font</span><span class="pun">-</span><span class="pln">weight</span><span class="pun">:</span><span class="pln"> </span><span class="lit">300</span><span class="pun">;</span><span class="pln">
  padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.8rem</span><span class="pun">;</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1rem</span><span class="pun">;</span><span class="pln">
  text</span><span class="pun">-</span><span class="pln">align</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">input__lg </span><span class="pun">{</span><span class="pln">
  padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2rem</span><span class="pun">;</span><span class="pln">
  border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> solid </span><span class="com">#000;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">input__lg</span><span class="pun">:</span><span class="pln">focus </span><span class="pun">{</span><span class="pln">
  border</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="com">#4d4d4d;</span><span class="pln">
  box</span><span class="pun">-</span><span class="pln">shadow</span><span class="pun">:</span><span class="pln"> inset </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">2px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">[</span><span class="kwd">class</span><span class="pun">*=</span><span class="str">"__lg"</span><span class="pun">]</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  display</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">inline</span><span class="pun">-</span><span class="pln">block</span><span class="pun">;</span><span class="pln">
  width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
  font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.9rem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">[</span><span class="kwd">class</span><span class="pun">*=</span><span class="str">"__lg"</span><span class="pun">]:</span><span class="kwd">not</span><span class="pun">(:</span><span class="kwd">last</span><span class="pun">-</span><span class="pln">child</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1rem</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="lit">@media</span><span class="pln"> screen </span><span class="kwd">and</span><span class="pln"> </span><span class="pun">(</span><span class="pln">min</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">620px</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">[</span><span class="kwd">class</span><span class="pun">*=</span><span class="str">"__lg"</span><span class="pun">]</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2.4rem</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">filters </span><span class="pun">{</span><span class="pln">
  width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
  margin</span><span class="pun">:</span><span class="pln"> unset </span><span class="kwd">auto</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">‏</span><span class="com">/* تنسيقات عناصر‫ Todo */</span><span class="pln">
</span><span class="pun">.</span><span class="pln">todo </span><span class="pun">{</span><span class="pln">
  display</span><span class="pun">:</span><span class="pln"> flex</span><span class="pun">;</span><span class="pln">
  flex</span><span class="pun">-</span><span class="pln">direction</span><span class="pun">:</span><span class="pln"> row</span><span class="pun">;</span><span class="pln">
  flex</span><span class="pun">-</span><span class="pln">wrap</span><span class="pun">:</span><span class="pln"> wrap</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">todo </span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  flex</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">todo</span><span class="pun">-</span><span class="pln">text </span><span class="pun">{</span><span class="pln">
  width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
  min</span><span class="pun">-</span><span class="pln">height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4.4rem</span><span class="pun">;</span><span class="pln">
  padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.4rem</span><span class="pln"> </span><span class="lit">0.8rem</span><span class="pun">;</span><span class="pln">
  border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> solid </span><span class="com">#565656;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">todo</span><span class="pun">-</span><span class="pln">text</span><span class="pun">:</span><span class="pln">focus </span><span class="pun">{</span><span class="pln">
  box</span><span class="pun">-</span><span class="pln">shadow</span><span class="pun">:</span><span class="pln"> inset </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">2px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">/* تنسيقات مربعات الاختيار */</span><span class="pln">
</span><span class="pun">.</span><span class="pln">c</span><span class="pun">-</span><span class="pln">cb </span><span class="pun">{</span><span class="pln">
  box</span><span class="pun">-</span><span class="pln">sizing</span><span class="pun">:</span><span class="pln"> border</span><span class="pun">-</span><span class="pln">box</span><span class="pun">;</span><span class="pln">
  font</span><span class="pun">-</span><span class="pln">family</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Arial</span><span class="pun">,</span><span class="pln"> sans</span><span class="pun">-</span><span class="pln">serif</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">-</span><span class="pln">webkit</span><span class="pun">-</span><span class="pln">font</span><span class="pun">-</span><span class="pln">smoothing</span><span class="pun">:</span><span class="pln"> antialiased</span><span class="pun">;</span><span class="pln">
  font</span><span class="pun">-</span><span class="pln">weight</span><span class="pun">:</span><span class="pln"> </span><span class="lit">400</span><span class="pun">;</span><span class="pln">
  font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.6rem</span><span class="pun">;</span><span class="pln">
  line</span><span class="pun">-</span><span class="pln">height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1.25</span><span class="pun">;</span><span class="pln">
  display</span><span class="pun">:</span><span class="pln"> block</span><span class="pun">;</span><span class="pln">
  position</span><span class="pun">:</span><span class="pln"> relative</span><span class="pun">;</span><span class="pln">
  min</span><span class="pun">-</span><span class="pln">height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">44px</span><span class="pun">;</span><span class="pln">
  padding</span><span class="pun">-</span><span class="pln">left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40px</span><span class="pun">;</span><span class="pln">
  clear</span><span class="pun">:</span><span class="pln"> left</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">c</span><span class="pun">-</span><span class="pln">cb </span><span class="pun">&gt;</span><span class="pln"> label</span><span class="pun">::</span><span class="pln">before</span><span class="pun">,</span><span class="pln">
</span><span class="pun">.</span><span class="pln">c</span><span class="pun">-</span><span class="pln">cb </span><span class="pun">&gt;</span><span class="pln"> input</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  box</span><span class="pun">-</span><span class="pln">sizing</span><span class="pun">:</span><span class="pln"> border</span><span class="pun">-</span><span class="pln">box</span><span class="pun">;</span><span class="pln">
  top</span><span class="pun">:</span><span class="pln"> </span><span class="pun">-</span><span class="lit">2px</span><span class="pun">;</span><span class="pln">
  left</span><span class="pun">:</span><span class="pln"> </span><span class="pun">-</span><span class="lit">2px</span><span class="pun">;</span><span class="pln">
  width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">44px</span><span class="pun">;</span><span class="pln">
  height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">44px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">c</span><span class="pun">-</span><span class="pln">cb </span><span class="pun">&gt;</span><span class="pln"> input</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">-</span><span class="pln">webkit</span><span class="pun">-</span><span class="pln">font</span><span class="pun">-</span><span class="pln">smoothing</span><span class="pun">:</span><span class="pln"> antialiased</span><span class="pun">;</span><span class="pln">
  cursor</span><span class="pun">:</span><span class="pln"> pointer</span><span class="pun">;</span><span class="pln">
  position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
  z</span><span class="pun">-</span><span class="pln">index</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
  margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  opacity</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">c</span><span class="pun">-</span><span class="pln">cb </span><span class="pun">&gt;</span><span class="pln"> label </span><span class="pun">{</span><span class="pln">
  font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> inherit</span><span class="pun">;</span><span class="pln">
  font</span><span class="pun">-</span><span class="pln">family</span><span class="pun">:</span><span class="pln"> inherit</span><span class="pun">;</span><span class="pln">
  line</span><span class="pun">-</span><span class="pln">height</span><span class="pun">:</span><span class="pln"> inherit</span><span class="pun">;</span><span class="pln">
  display</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">inline</span><span class="pun">-</span><span class="pln">block</span><span class="pun">;</span><span class="pln">
  margin</span><span class="pun">-</span><span class="pln">bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">8px</span><span class="pln"> </span><span class="lit">15px</span><span class="pln"> </span><span class="lit">5px</span><span class="pun">;</span><span class="pln">
  cursor</span><span class="pun">:</span><span class="pln"> pointer</span><span class="pun">;</span><span class="pln">
  touch</span><span class="pun">-</span><span class="pln">action</span><span class="pun">:</span><span class="pln"> manipulation</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">c</span><span class="pun">-</span><span class="pln">cb </span><span class="pun">&gt;</span><span class="pln"> label</span><span class="pun">::</span><span class="pln">before </span><span class="pun">{</span><span class="pln">
  content</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">
  position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
  border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> solid currentColor</span><span class="pun">;</span><span class="pln">
  background</span><span class="pun">:</span><span class="pln"> transparent</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">c</span><span class="pun">-</span><span class="pln">cb </span><span class="pun">&gt;</span><span class="pln"> input</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]:</span><span class="pln">focus </span><span class="pun">+</span><span class="pln"> label</span><span class="pun">::</span><span class="pln">before </span><span class="pun">{</span><span class="pln">
  border</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4px</span><span class="pun">;</span><span class="pln">
  outline</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3px</span><span class="pln"> dashed </span><span class="com">#228bec;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">c</span><span class="pun">-</span><span class="pln">cb </span><span class="pun">&gt;</span><span class="pln"> label</span><span class="pun">::</span><span class="pln">after </span><span class="pun">{</span><span class="pln">
  box</span><span class="pun">-</span><span class="pln">sizing</span><span class="pun">:</span><span class="pln"> content</span><span class="pun">-</span><span class="pln">box</span><span class="pun">;</span><span class="pln">
  content</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">
  position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
  top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">11px</span><span class="pun">;</span><span class="pln">
  left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">9px</span><span class="pun">;</span><span class="pln">
  width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">18px</span><span class="pun">;</span><span class="pln">
  height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">7px</span><span class="pun">;</span><span class="pln">
  transform</span><span class="pun">:</span><span class="pln"> rotate</span><span class="pun">(-</span><span class="lit">45deg</span><span class="pun">);</span><span class="pln">
  border</span><span class="pun">:</span><span class="pln"> solid</span><span class="pun">;</span><span class="pln">
  border</span><span class="pun">-</span><span class="pln">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">5px</span><span class="pln"> </span><span class="lit">5px</span><span class="pun">;</span><span class="pln">
  border</span><span class="pun">-</span><span class="pln">top</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> transparent</span><span class="pun">;</span><span class="pln">
  opacity</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  background</span><span class="pun">:</span><span class="pln"> transparent</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">.</span><span class="pln">c</span><span class="pun">-</span><span class="pln">cb </span><span class="pun">&gt;</span><span class="pln"> input</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"checkbox"</span><span class="pun">]:</span><span class="kwd">checked</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> label</span><span class="pun">::</span><span class="pln">after </span><span class="pun">{</span><span class="pln">
  opacity</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	احفظ الملف وألقِ نظرةً على المتصفح، إذ يجب أن يتمتع تطبيقك الآن بتنسيق مقبول.
</p>

<h2>
	الخلاصة
</h2>

<p>
	يبدو تطبيق قائمة المهام الآن أشبه بتطبيق حقيقي، ولكنه لا يفعل أيّ شيء فعليًا، إذ سنبدأ في إصلاح ذلك لاحقًا في المقالات القادمة.
</p>

<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_todo_list_beginning" rel="external nofollow">Beginning our React todo list</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1071/" rel="">أساسيات بناء تطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-react-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-react-components-r834/" rel="">مكونات React الأساسية (React Components)</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/laravel/%d8%a5%d9%86%d8%b4%d8%a7%d8%a1-%d8%aa%d8%b7%d8%a8%d9%8a%d9%82-%d9%82%d8%a7%d8%a6%d9%85%d8%a9-%d9%85%d9%87%d8%a7%d9%85-%d8%a8%d8%b3%d9%8a%d8%b7-%d8%a8%d8%a7%d8%b3%d8%aa%d8%ae%d8%af%d8%a7%d9%85-laravel-5-%d8%a7%d9%84%d8%ac%d8%b2%d8%a1-%d8%a7%d9%84%d8%b1%d8%a7%d8%a8%d8%b9-r162/" rel="">إنشاء تطبيق قائمة مهام بسيط باستخدام Laravel 5 </a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1570</guid><pubDate>Thu, 23 Jun 2022 15:01:03 +0000</pubDate></item><item><title>&#x628;&#x62F;&#x621; &#x627;&#x644;&#x639;&#x645;&#x644; &#x645;&#x639; &#x645;&#x643;&#x62A;&#x628;&#x629; React</title><link>https://academy.hsoub.com/programming/javascript/react/%D8%A8%D8%AF%D8%A1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-react-r1569/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_05/628239a93f617_-----React.png.824f17703c475fcd280222eb210268cb.png" /></p>
<p>
	سنلقي في هذا المقال نظرةً على مكتبة React، إذ سنطّلع على بعض التفاصيل حول خلفيتها وحالات استخدامها، وسننشئ سلسلة أدوات React الأساسية وتطبيقًا بسيطًا بحيث نتعلم كيفية عمل React.
</p>

<ul>
	<li>
		<strong>المتطلبات الأساسية</strong>: الإلمام بأساسيات لغات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و<a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a> و<a href="https://wiki.hsoub.com/JavaScript" rel="external">جافاسكربت JavaScript</a> ومعرفة استخدام <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471/" rel="">سطر الأوامر أو الطرفية</a>، إذ تستخدِم <a href="https://wiki.hsoub.com/React" rel="external">React</a> صيغة لغة HTML ضمن جافاسكربت HTML-in-JavaScript، والتي تسمى <a href="https://wiki.hsoub.com/React/introducing_jsx" rel="external">JSX</a>، أي JavaScript وXML، كما سيساعدك التعرف على كل من لغة HTML وجافاسكربت على تعلّم صيغة JSX، وتحديد ما إذا كانت الأخطاء في تطبيقك مرتبطةً بجافاسكربت أو بمجال أكثر تحديدًا من React.
	</li>
	<li>
		<strong>الهدف</strong>: إعداد بيئة تطوير React المحلية، وإنشاء تطبيق بسيط، وفهم أساسيات عمله.
	</li>
</ul>

<p>
	تُعَدّ <a href="https://reactjs.org/" rel="external nofollow">React</a> مكتبةً لبناء واجهات المستخدِم، ولا تُعَدّ إطار عمل، فهي ليست حصريةً للويب، كما تُستخدَم مكتبة React مع المكتبات الأخرى للتصيير Render إلى بيئات معينة، إذ يمكن استخدام إطار عمل <a href="https://wiki.hsoub.com/ReactNative" rel="external">React Native</a> لبناء تطبيقات الهاتف المحمول، لكن يستخدِم المطورون مكتبة React جنبًا إلى جنب مع <a href="https://wiki.hsoub.com/React#.D8.A7.D9.84.D9.83.D8.A7.D8.A6.D9.86_ReactDOM" rel="external">ReactDOM</a> للبناء للويب، إذ تُستخدَم React و ReactDOM في المجالات نفسها ولحل المشكلات نفسها التي تستخدِمها أطر تطوير الويب الحقيقية الأخرى، لذلك نشير إلى React بوصفها إطار عمل Framework.
</p>

<p>
	تهدف React إلى تقليل الأخطاء التي تحدث عندما يبني المطورون واجهات المستخدِم من خلال استخدام المكوّنات Components، والتي تُعَدّ أجزاءً من الشيفرة البرمجية المنطقية والمستقلة ذاتيًا والتي تصف جزءًا من واجهة المستخدِم، إذ يمكن تكوين هذه المكونات مع بعضها البعض لإنشاء واجهة مستخدِم كاملة، كما تجرِّّد React كثيرًا من أعمال التصيير، وبالتالي تجعلك تركِّز على تصميم واجهة المستخدِم.
</p>

<h2>
	حالات الاستخدام Use cases
</h2>

<p>
	لا تفرض React قواعد صارمةً حول اصطلاحات الشيفرة أو تنظيم الملفات خلاف <a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">أطر العمل Frameworks</a> الأخرى، مما يتيح لفرق العمل تحديد الاصطلاحات التي تناسبها بصورة أفضل، واستخدام مكتبة React بالطريقة التي ترغب بها، إذ يمكن لمكتبة React معالجة زر واحد أو أجزاء من الواجهة أو واجهة المستخدِم للتطبيق بأكمله، فإذا أردت استخدام React لأجزاء صغيرة من الواجهة، فلا يُعَدّ ذلك سهلًا مثل بناء تطبيق باستخدام مكتبة مثل <a href="https://wiki.hsoub.com/jQuery" rel="external">jQuery</a> أو إطار عمل مثل <a href="https://academy.hsoub.com/programming/javascript/vuejs/" rel="">Vue</a>، إذ يكون استخدام مكتبة React أسهل عند إنشاء تطبيقك بالكامل باستخدامها.
</p>

<p>
	كما تتطلب العديد من مزايا تجربة المطوِّر لتطبيق React مثل كتابة الواجهات باستخدام صيغة JSX، عملية تصريف Compilation، في حين تبطّئ إضافة مصرِّف مثل Babel إلى موقع ويب الشيفرة الموجودة عليه، لذلك يُعِدّ المطورون مثل هذه الأدوات باستخدام خطوة بناء، إذ يمكن القول أنّ React لها متطلبات أدوات كثيرة، ولكن يمكن تعلّمها، كما سيركِّز هذا المقال على حالة استخدام React لتصيير واجهة المستخدِم بالكامل لتطبيق ما باستخدام الأدوات التي توفرها أداة <a href="https://create-react-app.dev/" rel="external nofollow">create-react-app</a> الخاصة بفيسبوك.
</p>

<h2>
	كيفية استخدام React للغة جافاسكربت
</h2>

<p>
	تستخدِم React ميزات لغة جافاسكربت الحديثة للعديد من أنماطها، ولكن يأتي أكبر تحوّل لها عن جافاسكربت عند استخدام صيغة JSX التي توسِّع صيغة جافاسكربت، بحيث يمكن أن تكون الشيفرة البرمجية التي تشبه HTML جنبًا إلى جنب معها، وإليك المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3406_8" style=""><span class="pln">const heading = </span><span class="tag">&lt;h1&gt;</span><span class="pln">Mozilla Developer Network</span><span class="tag">&lt;/h1&gt;</span><span class="pln">;</span></pre>

<p>
	يُعرَف الثابت <code>heading</code> السابق بتعبير JSX، ويمكن لمكتبة React استخدامه لتصيير الوسم <code>&lt;h1&gt;</code> في التطبيق، ولنفترض أننا أردنا تغليف العنوان <code>heading</code> بوسم <code>&lt;header&gt;</code> لأسباب دلالية، إذ تتيح صيغة JSX بتداخل العناصر ضمن بعضها بعضًا كما نفعل مع لغة HTML كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3406_10" style=""><span class="pln">const header = (
  </span><span class="tag">&lt;header&gt;</span><span class="pln">
    </span><span class="tag">&lt;h1&gt;</span><span class="pln">Mozilla Developer Network</span><span class="tag">&lt;/h1&gt;</span><span class="pln">
  </span><span class="tag">&lt;/header&gt;</span><span class="pln">
);</span></pre>

<p>
	ملاحظة: لا تُعَدّ الأقواس في المقتطف السابق خاصةً بصيغة JSX، وليس لها أيّ تأثير على تطبيقك، وإنما تُعَدّ إشارةً لك ولحاسوبك بأن الأسطر المتعددة من الشيفرة البرمجية الموجودة ضمنها هي جزء من التعبير نفسه، كما يمكنك كتابة تعبير <code>header</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3406_12" style=""><span class="kwd">const</span><span class="pln"> header </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">header</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="typ">Mozilla</span><span class="pln"> </span><span class="typ">Developer</span><span class="pln"> </span><span class="typ">Network</span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">header</span><span class="pun">&gt;</span></pre>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		لكن يجب الانتباه إلى عدم وضع مسافة بادئة لوسم <code>&lt;header&gt;</code> الذي يبدأ التعبير في الموضع نفسه لوسم الإغلاق المقابل له.
	</p>
</blockquote>

<p>
	لا يمكن لمتصفحك قراءة صيغة JSX بدون مساعدة، إذ سيبدو التعبير <code>header</code> كما يلي عند تصريفه باستخدام أداة <a href="https://babeljs.io/" rel="external nofollow">Babel</a> أو <a href="https://parceljs.org/" rel="external nofollow">Parcel</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3406_14" style=""><span class="kwd">const</span><span class="pln"> header </span><span class="pun">=</span><span class="pln"> </span><span class="typ">React</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"header"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">React</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"h1"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Mozilla Developer Network"</span><span class="pun">)</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	يمكن تخطي خطوة التصريف واستخدام التابع <code>React.createElement()‎</code> لكتابة واجهة المستخدِم بنفسك، ولكنك تفقد بذلك ميزة JSX التصريحية، وتصبح قراءة شيفرتك أصعب، إذ يُعَدّ التصريف خطوةً إضافيةً في عملية التطوير، في حين يعتقد العديد من المطورين في مجتمع React أنّ قابلية قراءة JSX تستحق العناء، كما تجعل الأدوات الشائعة تصريف صيغة JSX إلى جافاسكربت جزءًا من عملية الإعداد، ولا يتعين عليك إعداد التصريف بنفسك إلّا إذا أردت ذلك.
</p>

<p>
	تُعَدّ صيغة JSX مزيجًا من لغتَي HTML وجافاسكربت، لذلك يجدها بعض المطورين سهلة التعلم، ويجدها آخرون مربكةً بسبب طبيعتها الممزوجة، ولكنها ستسمح لك ببناء واجهات مستخدِم بسرعة وبسهولة إذا أتقنتها، كما ستسمح للآخرين بفهم قاعدة شيفرتك البرمجية فهمًا أفضل وبسرعة، كما يمكنك الاطلاع على صفحة <a href="https://wiki.hsoub.com/React/jsx_in_depth" rel="external">شرح JSX بالتفصيل</a> من توثيق React في موسوعة حسوب لقراءة المزيد عن JSX.
</p>

<h2>
	إعداد تطبيق React الأول
</h2>

<p>
	هناك العديد من الطرق لاستخدام React، لكننا سنستخدِم <code>create-react-app</code> وهي أداة واجهة سطر الأوامر -أو CLI اختصارًا-، إذ تسرّع هذه الأداة عملية تطوير تطبيق React عن طريق تثبيت بعض الحزم وإنشاء بعض الملفات، والتعامل مع الأدوات الموضَّحة سابقًا، كما يمكن <a href="https://wiki.hsoub.com/React/add_react_to_a_website" rel="external">إضافة React إلى موقع ويب دون استخدام الأداة <code>create-react-app</code></a> عن طريق نسخ بعض عناصر <code>&lt;script&gt;</code> في ملف HTML، ولكن تُعَدّ الأداة <code>create-react-app</code> نقطة بداية شائعة لتطبيقات React، إذ سيسمح لك استخدامها بقضاء المزيد من الوقت في بناء تطبيقك ووقت أقل في التفكير في الإعداد.
</p>

<h3>
	المتطلبات
</h3>

<p>
	يجب تثبيت <a href="https://nodejs.org/en/" rel="external nofollow">Node.js</a> من أجل استخدام <code>create-react-app</code>، كما يوصى باستخدام إصدار الدعم طويل الأمد Long-term Support -أو LTS اختصارًا-، إذ يتضمن Node <a href="https://academy.hsoub.com/programming/javascript/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-r1225/" rel="">مدير الحزم npm</a> ومشغّل الحزم npx، كما يمكنك استخدام مدير الحزم Yarn، لكننا سنفترض أنك تستخدِم npm في هذا المقال، وهنا يمكنك الاطلاع على مقال <a href="https://academy.hsoub.com/programming/workflow/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1472/" rel="">أساسيات إدارة الحزم</a> لمزيد من المعلومات حول npm وYarn.
</p>

<p>
	إذا استخدمت نظام ويندوز Windows، فستحتاج إلى تثبيت بعض البرامج التي تمنحك التكافؤ مع طرفية نظام يونيكس Unix أو نظام ماك macOS لاستخدام أوامر الطرفية التي سنستخدِمها، إذ يُعَدّ كل من Gitbash الذي يكون جزءًا من مجموعة أدوات <a href="https://gitforwindows.org/" rel="external nofollow">git لنظام ويندوز</a> أو <a href="https://docs.microsoft.com/en-us/windows/wsl/about" rel="external nofollow">نظام ويندوز الفرعي للينكس Windows Subsystem for Linux</a> -أو WSL اختصارًا- مناسبَين، كما يمكنك الاطلاع على <a href="https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1471" rel="">دليل استخدام سطر الأوامر</a> للحصول على مزيد من المعلومات حول هذه الأوامر وحول أوامر الطرفية بصفة عامة.
</p>

<p>
	ضع في بالك أنّ React و ReactDOM ينتجان تطبيقات تعمل فقط على مجموعة حديثة إلى حد ما من المتصفحات مثل IE9+‎ باستخدام تعويض نقص دعم المتصفحات Polyfill، كما يوصَى باستخدام متصفح حديث مثل فايرفوكس Firefox أو مايكروسوفت إيدج Microsoft Edge أو سفاري Safari أو كروم Chrome.
</p>

<h3>
	تهيئة التطبيق
</h3>

<p>
	تأخذ الأداة <code>create-react-app</code> وسيطًا واحدًا هو الاسم الذي ترغب في منحه لتطبيقك، وتستخدِمه لإنشاء مجلد جديد، ثم تنشئ الملفات الضرورية بداخله، وتأكد من تطبيق الأمر <code>cd</code> على المكان الذي تريد أن يكون فيه تطبيقك على القرص الصلب، ثم شغّل الأمر التالي في الطرفية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3406_19" style=""><span class="pln">npx create</span><span class="pun">-</span><span class="pln">react</span><span class="pun">-</span><span class="pln">app moz</span><span class="pun">-</span><span class="pln">todo</span><span class="pun">-</span><span class="pln">react</span></pre>

<p>
	يؤدي تشغيل الأمر السابق إلى إنشاء المجلد moz-todo-response، مع تنفيذ الأمور التالية ضمنه:
</p>

<ul>
	<li>
		تثبيت بعض حزم npm الأساسية لعمل التطبيق.
	</li>
	<li>
		كتابة سكربتات لبدء التطبيق وتنفيذه.
	</li>
	<li>
		إنشاء بنية من الملفات والمجلدات التي تحدِّد معمارية التطبيق الأساسية.
	</li>
	<li>
		تهيئة المجلد بوصفه مستودع جيت git إذا كان جيت مثبتًا على حاسوبك.
	</li>
</ul>

<p>
	ملاحظة: إذا كان مدير الحزم yarn مثبتًا لديك، فستُستخدَم أداة <code>create-react-app</code> افتراضيًا لاستخدام yarn بدلًا من npm، وإذا كان كل من مديرَي الحزم مثبَّتَين لديك وتريد استخدام npm صراحةً، فيمكنك إضافة الراية <code>‎--use-npm</code> عند تشغيل <code>create-react-app</code>:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_3406_21" style=""><span class="pln">npx create</span><span class="pun">-</span><span class="pln">react</span><span class="pun">-</span><span class="pln">app moz</span><span class="pun">-</span><span class="pln">todo</span><span class="pun">-</span><span class="pln">react </span><span class="pun">--</span><span class="kwd">use</span><span class="pun">-</span><span class="pln">npm</span></pre>

<p>
	ستعرِض <code>create-react-app</code> عددًا من الرسائل في الطرفية أثناء عملها، وهذا أمر طبيعي، إذ يمكن أن يستغرق ذلك بضع دقائق، فالوقت مناسب الآن لتحضير كوب من الشاي.
</p>

<p>
	غيّر المسار الحالي إلى المجلد moz-todo-react باستخدام الأمر <code>cd</code> عند اكتمال العملية، ثم شغّل الأمر <code>npm start</code>، إذ سيبدأ تشغيل السكربتات المُثبَّتة باستخدام الأداة <code>create-react-app</code> على خادم محلي على المضيف المحلي localhost الذي هو 3000، وافتح التطبيق في تبويب جديد من المتصفح، إذ سيعرِض متصفحك ما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="" rel=""><span></span></a>
</p>

<h3>
	بنية التطبيق
</h3>

<p>
	تمنحنا أداة <code>create-react-app</code> كل ما نحتاجه لتطوير تطبيق React، إذ تبدو بنية الملفات الأولية الخاصة به كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3406_28" style=""><span class="pln">moz</span><span class="pun">-</span><span class="pln">todo</span><span class="pun">-</span><span class="pln">react
</span><span class="pun">├──</span><span class="pln"> README</span><span class="pun">.</span><span class="pln">md
</span><span class="pun">├──</span><span class="pln"> node_modules
</span><span class="pun">├──</span><span class="pln"> package</span><span class="pun">.</span><span class="pln">json
</span><span class="pun">├──</span><span class="pln"> package</span><span class="pun">-</span><span class="pln">lock</span><span class="pun">.</span><span class="pln">json
</span><span class="pun">├──</span><span class="pln"> </span><span class="pun">.</span><span class="pln">gitignore
</span><span class="pun">├──</span><span class="pln"> </span><span class="kwd">public</span><span class="pln">
</span><span class="pun">│</span><span class="pln">   </span><span class="pun">├──</span><span class="pln"> favicon</span><span class="pun">.</span><span class="pln">ico
</span><span class="pun">│</span><span class="pln">   </span><span class="pun">├──</span><span class="pln"> index</span><span class="pun">.</span><span class="pln">html
</span><span class="pun">│</span><span class="pln">   </span><span class="pun">├──</span><span class="pln"> logo192</span><span class="pun">.</span><span class="pln">png
</span><span class="pun">│</span><span class="pln">   </span><span class="pun">├──</span><span class="pln"> logo512</span><span class="pun">.</span><span class="pln">png
</span><span class="pun">│</span><span class="pln">   </span><span class="pun">├──</span><span class="pln"> manifest</span><span class="pun">.</span><span class="pln">json
</span><span class="pun">│</span><span class="pln">   </span><span class="pun">└──</span><span class="pln"> robots</span><span class="pun">.</span><span class="pln">txt
</span><span class="pun">└──</span><span class="pln"> src
    </span><span class="pun">├──</span><span class="pln"> </span><span class="typ">App</span><span class="pun">.</span><span class="pln">css
    </span><span class="pun">├──</span><span class="pln"> </span><span class="typ">App</span><span class="pun">.</span><span class="pln">js
    </span><span class="pun">├──</span><span class="pln"> </span><span class="typ">App</span><span class="pun">.</span><span class="pln">test</span><span class="pun">.</span><span class="pln">js
    </span><span class="pun">├──</span><span class="pln"> index</span><span class="pun">.</span><span class="pln">css
    </span><span class="pun">├──</span><span class="pln"> index</span><span class="pun">.</span><span class="pln">js
    </span><span class="pun">├──</span><span class="pln"> logo</span><span class="pun">.</span><span class="pln">svg
    </span><span class="pun">├──</span><span class="pln"> reportWebVitals</span><span class="pun">.</span><span class="pln">js
    </span><span class="pun">└──</span><span class="pln"> setupTests</span><span class="pun">.</span><span class="pln">js</span></pre>

<p>
	يُعَدّ المجلد src بأنه المكان الذي سنقضي فيه معظم وقتنا، فهو مكان وجود شيفرة تطبيقنا البرمجية، كما يحتوي المجلد public على ملفات سيقرأها متصفحك أثناء تطوير التطبيق وأهمها index.html، إذ تحقن React شيفرتك البرمجية في هذا الملف ليتمكّن متصفحك من تشغيلها، وهناك بعض الوسوم الأخرى التي تساعد الأداة <code>create-react-app</code> في عملها، لذا احرص على عدم تعديلها إلا إذا كنت متأكدًا مما تفعله، ولكن يجب عليك تغيير النص الموجود داخل العنصر <code>&lt;title&gt;</code> في هذا الملف ليعكس عنوان تطبيقك، وعناوين الصفحات الدقيقة مهمة من أجل إمكانية الوصول.
</p>

<p>
	سيُنشَر أيضًا المجلد public عند إنشاء ونشر إصدار الإنتاج من تطبيقك، إذ لن نغطّي مرحلة النشر في هذا المقال، ولكن يجب أن تكون قادرًا على استخدام حل مشابه لذلك الموضَّح في <a href="https://academy.hsoub.com/devops/deployment/%D9%85%D8%B1%D8%AD%D9%84%D8%A9-%D9%86%D8%B4%D8%B1-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r589/" rel="">مقال نشر التطبيق</a>، في حين يحتوي الملف package.json على معلومات حول مشروعنا، والتي يستخدِمها كل من Node.js وnpm لإبقائه منظمًّا، كما لا يُعَدّ هذا الملف خاصًا بتطبيقات React، ولا تحتاج إلى فهم هذا الملف على الإطلاق لإكمال هذا المقال، ولكن إذا أردتَ معرفة المزيد عنه، فيمكنك قراءة مقال <a href="https://academy.hsoub.com/programming/workflow/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1472/" rel="">أساسيات إدارة الحزم</a>.
</p>

<h2>
	استكشاف مكون React الأول <app></app>
</h2>

<p>
	يُعَدّ المكوّن Component في React وحدةً قابلةً لإعادة الاستخدام والتي تصيّر جزءًا من التطبيق، كما يمكن أن تكون هذه الأجزاء كبيرةً أو صغيرةً، لكنها تكون عادةً محددةً بوضوح، فهي تخدم غرضًا واحدًا واضحًا، ولنفتح الملف src/App.js، لأنّ متصفحنا يطالبنا بتعديله، إذ يحتوي هذا الملف على المكوِّن الأول <code>App</code> وعدد قليل من سطور الشيفرة البرمجية الأخرى:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3406_26" style=""><span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> logo from </span><span class="str">'./logo.svg'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="str">'./App.css'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> </span><span class="typ">App</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"App"</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">header className</span><span class="pun">=</span><span class="str">"App-header"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">img src</span><span class="pun">={</span><span class="pln">logo</span><span class="pun">}</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"App-logo"</span><span class="pln"> alt</span><span class="pun">=</span><span class="str">"logo"</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">Edit</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">code</span><span class="pun">&gt;</span><span class="pln">src</span><span class="pun">/</span><span class="typ">App</span><span class="pun">.</span><span class="pln">js</span><span class="pun">&lt;/</span><span class="pln">code</span><span class="pun">&gt;</span><span class="pln"> and save to reload</span><span class="pun">.</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">a
          className</span><span class="pun">=</span><span class="str">"App-link"</span><span class="pln">
          href</span><span class="pun">=</span><span class="str">"https://reactjs.org"</span><span class="pln">
          target</span><span class="pun">=</span><span class="str">"_blank"</span><span class="pln">
          rel</span><span class="pun">=</span><span class="str">"noopener noreferrer"</span><span class="pln">
        </span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">Learn</span><span class="pln"> </span><span class="typ">React</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">a</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">header</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span><span class="pun">;</span></pre>

<p>
	يتكون ملف App.js من ثلاثة أجزاء رئيسية وهي كما يلي، إذ تتبع معظم مكونات React هذا النمط:
</p>

<ul>
	<li>
		بعض تعليمات الاستيراد <code>import</code> في الأعلى.
	</li>
	<li>
		المكوِّن <code>App</code> في المنتصف.
	</li>
	<li>
		تعليمة تصدير <code>export</code> في الأسفل.
	</li>
</ul>

<h3>
	تعليمات الاستيراد Import
</h3>

<p>
	تسمح تعليمات الاستيراد الموجودة في أعلى الملف App.js باستخدام الشيفرة المُعرَّفة في مكان آخر، وهذه التعليمات هي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3406_30" style=""><span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> logo from </span><span class="str">'./logo.svg'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="str">'./App.css'</span><span class="pun">;</span></pre>

<p>
	تستورِد التعليمة الأولى مكتبة React التي تحوِّل صيغة JSX التي نكتبها إلى التابع <code>React.createElement()‎</code>، ويجب على جميع مكونّات React استيراد وحدة <code>React</code>، فإذا تخطيت هذه الخطوة، فسيعطي تطبيقك خطأً، في حين تستورِد التعليمة الثانية صورة شعار Logo من الملف '‎./logo.svg'، ولاحظ استخدام <code>/.</code> في بداية المسار، والامتداد <code>‎.svg</code> في نهايته، إذ يدل ذلك على أن الملف محلي وأنه ليس ملف جافاسكربت، ويوجد الملف logo.svg في مجلدنا المصدر، ولا نكتب مسارًا أو امتدادًا عند استيراد وحدة <code>React</code>، لأنه لا يُعَدّ ملفًا محليًا، وإنما يُدرَج بوصفه اعتماديةً Dependency في الملف package.json.
</p>

<p>
	تستورِد التعليمة الثالثة ملف CSS المتعلق بالمكوّن <code>App</code>، ولاحظ عدم وجود اسم متغير والموجِّه <code>from</code>، إذ لا تُعَدّ هذه الصيغة أصيلةً Native في صيغة وحدة جافاسكربت، وإنما تأتي من أداة Webpack وهي الأداة التي تستخدِمها <code>create-react-app</code> لتجميع جميع ملفات جافاسكربت مع بعضها بعضًا وتقديمها إلى المتصفح.
</p>

<h3>
	المكون App
</h3>

<p>
	توجد دالة تسمَّى <code>App</code> بعد تعليمات الاستيراد، إذ يفضِّل مجتمع جافاسكربت استخدام الأسماء بحالة الجَمل Camel-case مثل <code>helloWorld</code>، في حين تستخدِم مكونات React أسماء المتغيرات بحالة باسكال Pascal-case مثل <code>HelloWorld</code> لتوضيح أنّ عنصر JSX المحدَّد هو مكون React وليس وسم HTML عادي، فإذا أردت إعادة تسمية الدالة <code>App</code> لتصبح <code>app</code>، فسيعطي متصفحك خطأً.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3406_32" style=""><span class="pln">function App() {
  return (
    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">className</span><span class="pun">=</span><span class="atv">"App"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;header</span><span class="pln"> </span><span class="atn">className</span><span class="pun">=</span><span class="atv">"App-header"</span><span class="tag">&gt;</span><span class="pln">
        </span><span class="tag">&lt;img</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">{logo}</span><span class="pln"> </span><span class="atn">className</span><span class="pun">=</span><span class="atv">"App-logo"</span><span class="pln"> </span><span class="atn">alt</span><span class="pun">=</span><span class="atv">"logo"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
        </span><span class="tag">&lt;p&gt;</span><span class="pln">
          Edit </span><span class="tag">&lt;code&gt;</span><span class="pln">src/App.js</span><span class="tag">&lt;/code&gt;</span><span class="pln"> and save to reload.
        </span><span class="tag">&lt;/p&gt;</span><span class="pln">
        </span><span class="tag">&lt;a</span><span class="pln">
          </span><span class="atn">className</span><span class="pun">=</span><span class="atv">"App-link"</span><span class="pln">
          </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"https://reactjs.org"</span><span class="pln">
          </span><span class="atn">target</span><span class="pun">=</span><span class="atv">"_blank"</span><span class="pln">
          </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"noopener noreferrer"</span><span class="pln">
        </span><span class="tag">&gt;</span><span class="pln">
          Learn React
        </span><span class="tag">&lt;/a&gt;</span><span class="pln">
      </span><span class="tag">&lt;/header&gt;</span><span class="pln">
    </span><span class="tag">&lt;/div&gt;</span><span class="pln">
  );
}</span></pre>

<p>
	تعيد الدالة <code>App</code> تعبير JSX الذي يحدِّد ما يصيّره متصفحك على DOM في النهاية، كما تحتوي بعض العناصر في هذا التعبير على سمات Attributes مكتوبةً كما تُكتَب في لغة HTML تمامًا باتباع النمط <code>attribute="value"‎</code>، في حين يحتوي وسم الفتح <code>&lt;div&gt;</code> على السمة <code>className</code> في السطر الثالث، وهي الخاصية <code>class</code> نفسها في لغة HTML، ولكن لا يمكننا استخدام الكلمة <code>class</code>، لأنّ صيغة JSX هي لغة جافاسكربت وهي كلمة محجوزة فيها، مما يعني أنّ لغة حافاسكربت تستخدِمها مسبقًا لغرض معيَّن، وقد يتسبّب استخدامها في شيفرتنا في حدوث مشاكل، كما تُكتَب بعض سمات HTML الأخرى بطريقة مختلفة في JSX عن تلك الموجودة في لغة HTML للسبب ذاته.
</p>

<p>
	عدِّل الوسم <code>&lt;p&gt;</code> في السطر السادس، بحيث يصبح "Hello, world!‎"، ثم احفظ ملفك، إذ ستلاحظ أن هذا التعديل سيُصيَّر مباشرةً في خادم التطوير الذي يعمل على المضيف المحلي <code><a href="http://localhost:3000" ipsnoembed="true" rel="external nofollow">http://localhost:3000</a></code> في متصفحك، ثم احذف بعد ذلك الوسم <code>&lt;a&gt;</code> واحفظ الملف، مما يؤدي إلى اختفاء رابط "Learn React"، إذ يجب أن يبدو المكوّن <code>App</code> الآن كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3406_34" style=""><span class="pln">function App() {
  return (
    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">className</span><span class="pun">=</span><span class="atv">"App"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;header</span><span class="pln"> </span><span class="atn">className</span><span class="pun">=</span><span class="atv">"App-header"</span><span class="tag">&gt;</span><span class="pln">
        </span><span class="tag">&lt;img</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">{logo}</span><span class="pln"> </span><span class="atn">className</span><span class="pun">=</span><span class="atv">"App-logo"</span><span class="pln"> </span><span class="atn">alt</span><span class="pun">=</span><span class="atv">"logo"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
        </span><span class="tag">&lt;p&gt;</span><span class="pln">
          Hello, World!
        </span><span class="tag">&lt;/p&gt;</span><span class="pln">
      </span><span class="tag">&lt;/header&gt;</span><span class="pln">
    </span><span class="tag">&lt;/div&gt;</span><span class="pln">
  );
}</span></pre>

<h3>
	تعليمات التصدير
</h3>

<p>
	تجعل تعليمة التصدير <code>export default App</code> في الجزء السفلي من الملف <code>App.js</code> المكوّنَ <code>App</code> متاحًا للوحدات الأخرى.
</p>

<h2>
	الملف index.js
</h2>

<p>
	لنفتح الملف src/index.js الذي يُعَدّ المكان الذي يُستخدَم فيه المكوّن <code>App</code>، وهو نقطة الدخول إلى تطبيقنا، إذ يبدو في البداية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3406_36" style=""><span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">ReactDOM</span><span class="pln"> from </span><span class="str">'react-dom'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="str">'./index.css'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> from </span><span class="str">'./App'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> as serviceWorker from </span><span class="str">'./serviceWorker'</span><span class="pun">;</span><span class="pln">

</span><span class="typ">ReactDOM</span><span class="pun">.</span><span class="pln">render</span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">React</span><span class="pun">.</span><span class="typ">StrictMode</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">App</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="typ">React</span><span class="pun">.</span><span class="typ">StrictMode</span><span class="pun">&gt;,</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">'root'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">);</span><span class="pln">

</span><span class="com">// إذا أردت أن يعمل تطبيقك في وضع عدم الاتصال وأن يُحمَّل بسرعة، فيمكنك تغيير</span><span class="pln">
</span><span class="com">// ‫unregister()‎ إلى register()‎ في الأسفل، ولاحظ أنّ هذا يأتي مع بعض المخاطر.</span><span class="pln">
serviceWorker</span><span class="pun">.</span><span class="pln">unregister</span><span class="pun">();</span></pre>

<p>
	يبدأ الملف index.js باستيراد جميع وحدات JS والملفات الأخرى التي يحتاجها للعمل كما هو الحال مع الملف App.js، ويحتفظ الملف src/index.css بالتنسيقات العامة المطبَّقة على تطبيقنا بالكامل، كما يمكننا رؤية المكوّن <code>App</code> الذي استوردناه، فهو متاح للاستيراد بفضل تعليمة التصدير <code>export</code> في أسفل الملف App.js، في يستدعي السطر السابع الدالة <code>ReactDOM.render()‎</code> مع وسيطين هما:
</p>

<ul>
	<li>
		المكوِّن الذي نريد تصييره، وهو <code>&lt;App /‎&gt;</code> في هذه الحالة.
	</li>
	<li>
		عنصر DOM الذي نريد تصيير المكوِّن ضمنه، وهو العنصر ذو المعرِّف <code>root</code> في هذه الحالة، فإذا نظرت ضمن الملف public/index.html، فستجد أن هذا العنصر هو <code>&lt;div&gt;</code> ضمن العنصر <code>&lt;body&gt;</code>.
	</li>
</ul>

<p>
	وهذا يعني أننا نريد تصيير تطبيق React الخاص بنا مع المكوِّن <code>App</code> بوصفه الجذر أو المكوِّن الأول.
</p>

<p>
	ملاحظة: يجب أن تحتوي مكونات React وعناصر HTML على شرطات إغلاق مائلة في صيغة JSX، إذ ستؤدي كتابة المكوّن <code>&lt;App&gt;</code> فقط أو الوسم <code>&lt;img&gt;</code> فقط إلى حدوث خطأ.
</p>

<p>
	تُعَدّ <a href="https://academy.hsoub.com/programming/general/%D9%85%D9%81%D9%87%D9%88%D9%85-service-worker-%D9%88%D8%AA%D8%A3%D8%AB%D9%8A%D8%B1%D9%87-%D9%81%D9%8A-%D8%A3%D8%AF%D8%A7%D8%A1-%D9%88%D8%A8%D9%86%D9%8A%D8%A9-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D9%88%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r833/" rel="">عمّال الخدمة Service workers</a> أجزاءً مثيرةً من الشيفرة البرمجية التي تحسّن أداء التطبيق وتسمح لميزات تطبيقات الويب بالعمل في وضع عدم الاتصال، لكننا لن نتحدّث عنها في هذا المقال، إذ يمكنك حذف السطر الخامس ومعظم الشيفرة الموجودة أسفله، وهنا يجب أن يبدو ملف index.js النهائي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3406_39" style=""><span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">ReactDOM</span><span class="pln"> from </span><span class="str">'react-dom'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="str">'./index.css'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> from </span><span class="str">'./App'</span><span class="pun">;</span><span class="pln">

</span><span class="typ">ReactDOM</span><span class="pun">.</span><span class="pln">render</span><span class="pun">(&lt;</span><span class="typ">App</span><span class="pln"> </span><span class="pun">/&gt;,</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">'root'</span><span class="pun">));</span></pre>

<h2>
	المتغيرات والخاصيات
</h2>

<p>
	سنستخدِم فيما يلي بعضًا من مهارات جافاسكربت لنتمكّن من تعديل المكوّنات والتعامل مع البيانات في React، إذ سنتحدث عن كيفية استخدام المتغيرات في JSX، وسنشرح الخاصيات Props التي تُعَدّ طريقةً لتمرير البيانات إلى المكوّن الذي يمكن الوصول إليه بعد ذلك باستخدام المتغيرات.
</p>

<h3>
	المتغيرات في JSX
</h3>

<p>
	لنركّز على السطر التاسع في الملف App.js:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3406_46" style=""><span class="tag">&lt;img</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">{logo}</span><span class="pln"> </span><span class="atn">className</span><span class="pun">=</span><span class="atv">"App-logo"</span><span class="pln"> </span><span class="atn">alt</span><span class="pun">=</span><span class="atv">"logo"</span><span class="pln"> </span><span class="tag">/&gt;</span></pre>

<p>
	وُضِعت قيمة السمة <code>src</code> الخاصة بالوسم <code>&lt;img /‎&gt;</code> ضمن أقواس معقوصة، وهي الطريقة التي تتعرف بها صيغة JSX على المتغيرات، إذ تشير القيمة <code>{logo}</code> إلى استيراد الشعار logo في السطر الثاني من التطبيق، ثم استرداد ملف الشعار وتصييره، ولنحاول إنشاء متغير خاص بنا من خلال إضافة التعليمة <code>const subject = 'React';‎</code> قبل تعليمة <code>return</code> في الدالة <code>App</code>، إذ يجب أن يبدو المكوّن <code>App</code> الآن كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3406_48" style=""><span class="kwd">function</span><span class="pln"> </span><span class="typ">App</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> subject </span><span class="pun">=</span><span class="pln"> </span><span class="str">"React"</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"App"</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">header className</span><span class="pun">=</span><span class="str">"App-header"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">img src</span><span class="pun">={</span><span class="pln">logo</span><span class="pun">}</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"App-logo"</span><span class="pln"> alt</span><span class="pun">=</span><span class="str">"logo"</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> </span><span class="typ">World</span><span class="pun">!</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">header</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	غيّر السطر الثامن لاستخدام المتغير <code>subject</code> بدلًا من االكلمة <code>"world"</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3406_50" style=""><span class="kwd">function</span><span class="pln"> </span><span class="typ">App</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> subject </span><span class="pun">=</span><span class="pln"> </span><span class="str">"React"</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div className</span><span class="pun">=</span><span class="str">"App"</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">header className</span><span class="pun">=</span><span class="str">"App-header"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">img src</span><span class="pun">={</span><span class="pln">logo</span><span class="pun">}</span><span class="pln"> className</span><span class="pun">=</span><span class="str">"App-logo"</span><span class="pln"> alt</span><span class="pun">=</span><span class="str">"logo"</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">subject</span><span class="pun">}!</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">header</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب أن يعرض المتصفح التعليمة <code>"Hello, React!‎"</code> بدلًا من التعليمة <code>"Hello, world!‎"</code> عند الحفظ، ولا يستفيد المتغير الذي ضبطناه للتو استفادةً كبيرةً من ميزات React، لذلك نحتاج إلى استخدام الخاصيات Props.
</p>

<h2>
	خاصيات المكون
</h2>

<p>
	الخاصية هي البيانات الممرَّرة إلى مكوِّن React، كما تشبه الخاصيات إلى حد ما سمات HTML، ولكن تمتلك عناصر HTML سمات وتمتلك مكونات React خاصيات، إذ تُكتَب الخاصيات ضمن استدعاءات المكوِّن، وتستخدِم الصيغة نفسها التي تستخدمها سمات HTML وهي <code>prop="value"‎</code>، كما يكون تدفّق البيانات أحادي الاتجاه في React، إذ يمكن تمرير الخاصيات من المكوّنات الآباء إلى المكوّنات الأبناء فقط، وتكون الخاصيات للقراءة فقط، فلنفتح الملف index.js ونمنح المكوّن <code>&lt;App/‎&gt;</code> استدعاءه الأول، ثم أضف الخاصية <code>subject</code> إلى استدعاء المكوِّن <code>&lt;App/‎&gt;</code> مع القيمة <code>Clarice</code>، إذ يجب أن تبدو شيفرتك البرمجية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3406_52" style=""><span class="pln">ReactDOM.render(</span><span class="tag">&lt;App</span><span class="pln"> </span><span class="atn">subject</span><span class="pun">=</span><span class="atv">"Clarice"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">, document.getElementById('root'));</span></pre>

<p>
	لنفتح الملف App.js ولننتقل إلى الدالة <code>App()‎</code> التي يجب أن تكون كما يلي مع اختصار تعليمة <code>return</code> للإيجاز:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3406_54" style=""><span class="kwd">function</span><span class="pln"> </span><span class="typ">App</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> subject </span><span class="pun">=</span><span class="pln"> </span><span class="str">"React"</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="com">// ‫تعليمة return</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عدّل الدالة <code>App</code> بحيث تقبل الخاصيات <code>props</code> على أساس معامِل لها، واحذف الثابت <code>subject</code>، كما يمكنك وضع الخاصيات <code>props</code> في التابع <code>console.log()‎</code> لطباعتها على طرفية المتصفح كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3406_56" style=""><span class="kwd">function</span><span class="pln"> </span><span class="typ">App</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">props</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="com">// ‫تعليمة return</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	احفظ ملفك وتحقق من طرفية جافاسكربت (نافذة console) في متصفحك، إذ يجب أن ترى شيئًا يشبه ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3406_58" style=""><span class="typ">Object</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> subject</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Clarice"</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	تتوافق خاصية الكائن <code>subject</code> مع الخاصية <code>subject</code> التي أضفناها إلى استدعاء المكون <code>&lt;App /‎&gt;</code>، كما تتوافق سلسلة <code>Clarice</code> النصية مع قيمتها، إذ تُجمَع خاصيات المكوِّن في React دائمًا ضمن كائنات بهذه الطريقة، ولنستخدِم الخاصية <code>subject</code> في الملف App.js، لذا غيّر الثابت <code>subject</code> لقراءة قيمة <code>props.subject</code> بدلًا من تعريفه على أنه سلسلة <code>React</code>، كما يمكنك حذف التابع <code>console.log()‎</code> إذا أردت ذلك.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3406_60" style=""><span class="kwd">function</span><span class="pln"> </span><span class="typ">App</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> subject </span><span class="pun">=</span><span class="pln"> props</span><span class="pun">.</span><span class="pln">subject</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="com">// تعليمة‫ return</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب أن يعرض التطبيق عبارة <code>"Hello, Clarice!‎"</code> عند الحفظ، فإذا عدت إلى الملف index.js وعدّلت قيمة <code>subject</code> ثم حفظته، فسيتغيّر النص.
</p>

<h2>
	الخلاصة
</h2>

<p>
	تعرّفنا في هذا المقال على مكتبة React، بما في ذلك كيفية تثبيتها محليًا، وإنشاء تطبيق بسيط، وكيفية عمل الأساسيات؛ أما في المقال التالي، فسنبدأ بإنشاء أول تطبيق مناسب وهو تطبيق قائمة المهام، لكن لنلخّص بعض الأشياء التي تعلمناها حتى الآن.
</p>

<p>
	في React:
</p>

<ul>
	<li>
		يمكن للمكونات استيراد الوحدات التي تحتاجها ويجب أن تصدِّر نفسها في الجزء السفلي من ملفاتها.
	</li>
	<li>
		تُسمَّى دوال المكوِّن باستخدام حالة باسكال PascalCase.
	</li>
	<li>
		يمكنك قراءة متغيرات JSX بوضعها بين أقواس معقوصة مثل <code>{so}</code>.
	</li>
	<li>
		تختلف بعض سمات JSX عن سمات HTML بحيث لا تتعارض مع كلمات جافاسكربت المحجوزة، إذ تُترجَم <code>class</code> في لغة HTML إلى <code>className</code> في JSX مثلًا، ولاحظ أنّ السمات متعددة الكلمات تُسمَّى باستخدام حالة الجَمل camel-cased.
	</li>
	<li>
		تُكتَب الخاصيات تمامًا مثل السمات ضمن استدعاءات المكوِّن وتُمرَّر إلى المكوّنات.
	</li>
</ul>

<p>
	ترجمة -وبتصرُّف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started" rel="external nofollow">Getting started with React</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-reactjs-%d9%85%d9%83%d8%aa%d8%a8%d8%a9-%d8%aa%d8%b7%d9%88%d9%8a%d8%b1-%d8%a7%d9%84%d9%88%d8%a7%d8%ac%d9%87%d8%a7%d8%aa-%d8%a7%d9%84%d8%b1%d8%b3%d9%88%d9%85%d9%8a%d8%a9-%d9%85%d9%86-%d9%81%d9%8a%d8%b3-%d8%a8%d9%88%d9%83-r112/" rel="">مدخل إلى React.js - مكتبة تطوير الواجهات الرسومية من فيس بوك</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D8%A9-react-router-r1166/" rel="">مدخل إلى استعمال المكتبة React-Router</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-react-%D8%A8%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-jest-%D9%88%D9%85%D9%83%D8%AA%D8%A8%D8%A9-react-testing-library-r1138/" rel="">اختبار تطبيقات React باستعمال Jest ومكتبة React Testing Library</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A7%D9%84%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-react-r774/" rel="">المصطلحات المستخدمة في React</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1569</guid><pubDate>Fri, 17 Jun 2022 15:07:00 +0000</pubDate></item><item><title>&#x623;&#x633;&#x627;&#x633;&#x64A;&#x627;&#x62A; React Native</title><link>https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-react-native-r1265/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_07/React-Native-basics.png.14cc3a32c0ff551b22fc33fd2e2ad02c.png" /></p>

<p>
	الآن وقد انتهينا من إعداد بيئة التطوير، سنتعرف على أساسيات React Native وسنبدأ بتطوير تطبيقنا. سنتعلم في هذا الفصل كيف سنبني واجهة مستخدم بالاستعانة بمكوّنات React Native البنيوية، وكيف سننسق مظهر هذه المكونات البنيوية، وكيف سننتقل من واجهة عرض إلى أخرى، كما سنتعلم كيف سندير حالة النماذج بفعالية.
</p>

<h2>
	المكونات البنيوية
</h2>

<p>
	تعلمنا في الأقسام السابقة طريقة استخدام React في تعريف المكوّنات كدوال تتلقى الخصائص كوسطاء وتعيد شجرة من عناصر React. تُمثَّل هذه الشجرة عادةً باستخدام شيفرة JSX. كما رأينا كيفية استخدام المكتبة <a href="https://reactjs.org/docs/react-dom.html" rel="external nofollow">ReactDOM</a> في بيئة المتصفح من أجل تحويل المكوّنات إلى شجرة DOM قابلة للتصيير ضمن المتصفح. لاحظ الشيفرة التالية التي تمثل مكوّنًا بسيطًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_7" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">HelloWorld</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> props </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="typ">Hello</span><span class="pln"> world</span><span class="pun">!&lt;/</span><span class="pln">div</span><span class="pun">&gt;;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	يعيد المكوّن <code>HelloWorld</code> عنصر <code>&lt;div&gt;</code> أُنشئ باستخدام عبارة JSX. وتذكر أن عبارة JSX ستُصرّف إلى استدعاءات للتابع <code>React.createElement</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_9" style="">
<span class="typ">React</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'div'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Hello world!'</span><span class="pun">);</span></pre>

<p>
	سيُنشئ سطر الشيفرة السابق عنصر <code>&lt;div&gt;</code> دون أية خصائص props وله عنصر ابن وحيد هو النص "Hello World". وعندما يُصيَّر هذا المكوّن إلى عنصر DOM جذري بتطبيق التابع <code>ReactDOM.render</code>، سيُصيَّر العنصر <code>&lt;div&gt;</code> إلى عنصر DOM المقابل.
</p>

<p>
	وهكذا نرى أن React لا ترتبط ببيئة محددة كبيئة المتصفح، لكن وبالاستعانة بمكتبات مثل ReactDOM ستتمكن من تصيير مجموعة من المكوّنات المعرّفة مسبقًا كعناصر DOM في بيئة محددة. ندعو هذه المكوّنات المعرّفة مسبقًا في React Native بالمكوّنات البنيوية core components.
</p>

<p>
	<a href="https://wiki.hsoub.com/ReactNative/intro_react_native_components" rel="external">فالمكوّنات البنيوية</a> هي مجموعة مكوّنات تؤمنها React Native قادرة على استخدام المكونات الأصلية لمنصة خلف الستار. لننفذ المثال السابق باستخدام React Native:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_11" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Text</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">HelloWorld</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> props </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="typ">Hello</span><span class="pln"> world</span><span class="pun">!&lt;/</span><span class="typ">Text</span><span class="pun">&gt;;};</span></pre>

<p>
	لاحظ كيف أدرجنا المكوّن <code>Text</code> من React Native، واستبدلنا العنصر<code>&lt;div&gt;</code> بالعنصر <code>&lt;text&gt;</code>. ستجد الكثير من عناصر DOM المقابلة لعناصر React Native، وسنستعرض بعض الأمثلة التي استقيناها من توثيق المكونات البنيوية:
</p>

<ul>
<li>
		المكوّن <a href="https://wiki.hsoub.com/ReactNative/text" rel="external">Text</a>: ويعتبر المكون الوحيد من مكونات React Native والذي يمتلك أبناء على شكل قيم نصية، ويشابه العناصر <code>&lt;h1&gt;</code> و<code>&lt;strong&gt;</code>.
	</li>
	<li>
		المكوّن <a href="https://wiki.hsoub.com/ReactNative/view" rel="external">View</a>: ويمثل وحدات البناء الرئيسية لواجهة المستخدم، ويشابه العنصر <code>&lt;div&gt;</code>.
	</li>
	<li>
		المكوّن <a href="https://wiki.hsoub.com/ReactNative/textinput" rel="external">TextInput</a>: وهو مكوّن لحقل نصي، ويشابه العنصر <code>&lt;input&gt;</code>.
	</li>
	<li>
		المكوّن <a href="https://wiki.hsoub.com/ReactNative/touchablewithoutfeedback" rel="external">TouchableWithoutFeedback</a>: وغيره من المكونات القابلة للمس Touchable ويستخدم لالتقاط مختلف حوادث الضغط والنقر، ويشابه العنصر <code>&lt;button&gt;</code>.
	</li>
</ul>
<p>
	هنالك بعض الاختلافات الواضحة بين المكوّنات البنيوية وعناصر DOM. يظهر الاختلاف الأول بأن المكوّن <code>Text</code> هو المكوّن الوحيد الذي يمتلك أبناء على شكل قيم نصية في React Native. وبمعنًى آخر، لن تستطيع استبدال هذا المكوّن بالمكوّن <code>View</code> مثلًا في المثال الذي أوردناه سابقًا.
</p>

<p>
	يتعلق الاختلاف الثاني بمعالجات الأحداث. فقد اعتدنا عند العمل مع عناصر DOM على إضافة معالج أحداث مثل <code>onClick</code> إلى أية عناصر مثل <code>&lt;div&gt;</code> و<code>&lt;btton&gt;</code>. لكن عليك قراءة <a href="https://wiki.hsoub.com/ReactNative/components_and_apis" rel="external">توثيق الواجهة البرمجية</a> في React Native لمعرفة أية معالجات وأية خصائص أخرى سيقبلها المكوّن الذي تستخدمه. إذ تؤمن -على سبيل المثال- عائلة مكوّنات اللمس <a href="https://wiki.hsoub.com/ReactNative/handling_touches#.D8.A7.D9.84.D9.85.D9.83.D9.88.D9.86.D8.A7.D8.AA_.D8.A7.D9.84.D9.82.D8.A7.D8.A8.D9.84.D8.A9_.D9.84.D9.84.D9.84.D9.85.D8.B3_.28Touchables.29" rel="external">Touchable components</a> القدرة على التقاط الحركات التي تُرسم على الشاشة، كما يمكنها إظهار استجابات محددة عند تمييز هذه الحركات. ومن هذه المكوّنات أيضًا <a href="https://wiki.hsoub.com/ReactNative/touchablewithoutfeedback" rel="external">TouchableWithoutFeedback</a> الذي يقبل الخاصية <code>onPress</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_13" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Text</span><span class="pun">,</span><span class="pln"> </span><span class="typ">TouchableWithoutFeedback</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Alert</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">TouchableText</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> props </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">TouchableWithoutFeedback</span><span class="pln">
      onPress</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">Alert</span><span class="pun">.</span><span class="pln">alert</span><span class="pun">(</span><span class="str">'You pressed the text!'</span><span class="pun">)}</span><span class="pln">
    </span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="typ">You</span><span class="pln"> can press me</span><span class="pun">&lt;/</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">TouchableWithoutFeedback</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	سنبدأ الآن في هيكلة مشروعنا بعد أن اطلعنا على أساسيات المكونات البنيوية. لننشئ مجلدًا باسم "src" في المجلد الجذري للمشروع، ولننشئ ضمنه مجلدًا آخر باسم "components". أنشئ ملفًا باسم "Main.js" وضعه في المجلد "components"، على أن يحتوي هذا الملف الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_15" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Constants</span><span class="pln"> from </span><span class="str">'expo-constants'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Text</span><span class="pun">,</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">,</span><span class="pln"> </span><span class="typ">View</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> styles </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">.</span><span class="pln">create</span><span class="pun">({</span><span class="pln">
  container</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    marginTop</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Constants</span><span class="pun">.</span><span class="pln">statusBarHeight</span><span class="pun">,</span><span class="pln">
    flexGrow</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
    flexShrink</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Main</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">container</span><span class="pun">}&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="typ">Rate</span><span class="pln"> </span><span class="typ">Repository</span><span class="pln"> </span><span class="typ">Application</span><span class="pun">&lt;/</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">Main</span><span class="pun">;</span></pre>

<p>
	لنستخدم الآن المكوّن <code>Main</code> ضمن المكوّن <code>App</code> الموجود في الملف "App.js" والذي يقع بدوره في المجلد الجذري للمشروع. استبدل محتويات الملف السابق بالشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_18" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Main</span><span class="pln"> from </span><span class="str">'./src/components/Main'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">Main</span><span class="pln"> </span><span class="pun">/&gt;;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span><span class="pun">;</span></pre>

<h2>
	إعادة تحميل التطبيق يدويا
</h2>

<p>
	رأينا كيف يعيد Expo تحميل التطبيق تلقائيًا عندما نجري تغييرات على الشيفرة. لكن قد تفشل عملية إعادة التحميل تلقائيًا في حالات معينة، وسنضطر إلى إعادة تحميل التطبيق يدويًا. تُنجز هذه العملية من خلال قائمة المطوّر التي يمكن الوصول إليها بهزّ جهازك أو باختيار الرابط "Shake Gesture" ضمن قائمة العتاد الصلب في محاكي iOS. كما يمكن أن تستخدم أيضًا الاختصار "D⌘" عندما يعمل تطبيقك على محاكي iOS، أو الاختصار "M⌘" عندما يعمل التطبيق على مقلّد Android في نظام macOS، والاختصار "Ctrl+M" لمقلد Android في نظامي Linux وWindows.
</p>

<p>
	اضغط على "Reload" عندما تفتح قائمة المطوّر لإعادة تحميل التطبيق. لن تحتاج بعد ذلك إلى إعادة تحميل التطبيق، بل ستحدث العملية تلقائيًا.
</p>

<h2>
	التمرين 10.3
</h2>

<h3>
	قائمة المستودعات المقيمة
</h3>

<p>
	سننجز في هذه التمارين النسخة الأولى من تطبيق قائمة المستودعات المُقيَّمة. ينبغي أن تتضمن القائمة الاسم الكامل للمستودع ووصفه ولغته وعدد التشعبات وعدد النجمات ومعدل التقييم وعدد التقييمات. ولحسن الحظ، تؤمن مكوّنًا مفيدًا لعرض قائمة من البيانات وهو <a href="https://wiki.hsoub.com/ReactNative/flatlist" rel="external">FlatList</a>.
</p>

<p>
	انجز المكوّنين <code>RepositoryList</code> و<code>RepositoryItem</code> في الملفين "RepositoryList.jsx" و"RepositoryItem.jsx" وضعهما في المجلد "componenets". ينبغي أن يصيّر المكوّن <code>RepositoryList</code> المكوّن البنيوي <a href="https://reactnative.dev/docs/flatlist" rel="external nofollow">FlatList</a> وأن يصيّر المكوّن <code>RepositoryItem</code> عنصرًا واحدًا من القائمة
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix">
		<p>
			تلميح: استخدم الخاصية العائدة للمكون <a href="https://wiki.hsoub.com/ReactNative/flatlist" rel="external">FlatList</a>.
		</p>
	</div>
</blockquote>

<p>
	استخدم ذلك كأساس للملف "RepositoryItem.jsx":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_20" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">FlatList</span><span class="pun">,</span><span class="pln"> </span><span class="typ">View</span><span class="pun">,</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> styles </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">.</span><span class="pln">create</span><span class="pun">({</span><span class="pln">
  separator</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> repositories </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'jaredpalmer.formik'</span><span class="pun">,</span><span class="pln">
    fullName</span><span class="pun">:</span><span class="pln"> </span><span class="str">'jaredpalmer/formik'</span><span class="pun">,</span><span class="pln">
    description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Build forms in React, without the tears'</span><span class="pun">,</span><span class="pln">
    language</span><span class="pun">:</span><span class="pln"> </span><span class="str">'TypeScript'</span><span class="pun">,</span><span class="pln">
    forksCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1589</span><span class="pun">,</span><span class="pln">
    stargazersCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">21553</span><span class="pun">,</span><span class="pln">
    ratingAverage</span><span class="pun">:</span><span class="pln"> </span><span class="lit">88</span><span class="pun">,</span><span class="pln">
    reviewCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln">
    ownerAvatarUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'https://avatars2.githubusercontent.com/u/4060187?v=4'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'rails.rails'</span><span class="pun">,</span><span class="pln">
    fullName</span><span class="pun">:</span><span class="pln"> </span><span class="str">'rails/rails'</span><span class="pun">,</span><span class="pln">
    description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Ruby on Rails'</span><span class="pun">,</span><span class="pln">
    language</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Ruby'</span><span class="pun">,</span><span class="pln">
    forksCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">18349</span><span class="pun">,</span><span class="pln">
    stargazersCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">45377</span><span class="pun">,</span><span class="pln">
    ratingAverage</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln">
    reviewCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln">
    ownerAvatarUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'https://avatars1.githubusercontent.com/u/4223?v=4'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'django.django'</span><span class="pun">,</span><span class="pln">
    fullName</span><span class="pun">:</span><span class="pln"> </span><span class="str">'django/django'</span><span class="pun">,</span><span class="pln">
    description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'The Web framework for perfectionists with deadlines.'</span><span class="pun">,</span><span class="pln">
    language</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Python'</span><span class="pun">,</span><span class="pln">
    forksCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">21015</span><span class="pun">,</span><span class="pln">
    stargazersCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">48496</span><span class="pun">,</span><span class="pln">
    ratingAverage</span><span class="pun">:</span><span class="pln"> </span><span class="lit">73</span><span class="pun">,</span><span class="pln">
    reviewCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">
    ownerAvatarUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'https://avatars2.githubusercontent.com/u/27804?v=4'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'reduxjs.redux'</span><span class="pun">,</span><span class="pln">
    fullName</span><span class="pun">:</span><span class="pln"> </span><span class="str">'reduxjs/redux'</span><span class="pun">,</span><span class="pln">
    description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Predictable state container for JavaScript apps'</span><span class="pun">,</span><span class="pln">
    language</span><span class="pun">:</span><span class="pln"> </span><span class="str">'TypeScript'</span><span class="pun">,</span><span class="pln">
    forksCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">13902</span><span class="pun">,</span><span class="pln">
    stargazersCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">52869</span><span class="pun">,</span><span class="pln">
    ratingAverage</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    reviewCount</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    ownerAvatarUrl</span><span class="pun">:</span><span class="pln"> </span><span class="str">'https://avatars3.githubusercontent.com/u/13142323?v=4'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">];</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">ItemSeparator</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">separator</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">FlatList</span><span class="pln">
      data</span><span class="pun">={</span><span class="pln">repositories</span><span class="pun">}</span><span class="pln">
      </span><span class="typ">ItemSeparatorComponent</span><span class="pun">={</span><span class="typ">ItemSeparator</span><span class="pun">}</span><span class="pln">
      </span><span class="com">// other props</span><span class="pln">
    </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pun">;</span></pre>

<p>
	لا تبدّل محتويات المتغيّر <code>repositories</code>، فمن المفترض أن يحتوي على كل ما تحتاج إليه لإكمال التمرين. صيّر المكوّن <code>ReositoryList</code> ضمن المكوّن <code>Main</code> الذي أضفناه سابقّا إلى الملف "Main.jsx". ستبدو قائمة الكتب المُقَّيمة مشابهةً للتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="71744" href="https://academy.hsoub.com/uploads/monthly_2021_07/reviewed_repo_list_01.png.16f3e53d3e7a7fc16f953248b0e561be.png" rel=""><img alt="reviewed_repo_list_01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="71744" data-unique="jn2r854gx" src="https://academy.hsoub.com/uploads/monthly_2021_07/reviewed_repo_list_01.thumb.png.1585294a8741f4be51f8736a0ad8d6a3.png"></a>
</p>

<h2>
	تنسيق التطبيق
</h2>

<p>
	بعد أن تعرفنا على آلية عمل المكوّنات، وكيفية استخدامها في بناء واجهة مستخدم بسيطة، لا بد من الالتفات إلى طرق تنسيق مظهر هذه التطبيقات. لقد رأينا في القسم 2 أنه من الممكن في بيئة المتصفح تعريف خصائص لتنسيق المكوّنات باستخدام CSS. كما رأينا أن هناك طريقتان لإنجاز ذلك: إما التنسيق ضمن السياق inline باستخدام الخاصية <code>style</code>، أو عن طريق محددات التنسيق المناسبة selectors المكتوبة في ملف CSS منفصل.
</p>

<p>
	ستجد تشابهًا واضحًا في الطريقة التي ترتبط بها خصائص التنسيق بمكونات React Native البنيوية والطريقة التي ترتبط بها هذه الخصائص بعناصر DOM. فمعظم مكوّنات React Native البنيوية تقبل الخاصية <code>style</code>، وتقبل هذه الخاصية بدورها كائنًا يحمل خصائص تنسيقٍ مع قيمها. وتتطابق في أغلب الأحيان خصائص التنسيق هذه مع مقابلاتها في CSS، مع اختلاف بسيط وهو كتابة أسماء هذه الصفات بطريقة سنام الجمل camelCase. أي ستكتب خصائص مثل "padding-top" على الشكل "paddingTop". وسيوضح المثال التالي طريقة استخدام الخاصية <code>style</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_22" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Text</span><span class="pun">,</span><span class="pln"> </span><span class="typ">View</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">BigBlueText</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pln"> style</span><span class="pun">={{</span><span class="pln"> padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">20</span><span class="pln"> </span><span class="pun">}}&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pln"> style</span><span class="pun">={{</span><span class="pln">
                   color</span><span class="pun">:</span><span class="pln"> </span><span class="str">'blue'</span><span class="pun">,</span><span class="pln">
                   fontSize</span><span class="pun">:</span><span class="pln"> </span><span class="lit">24</span><span class="pun">,</span><span class="pln">
                   fontWeight</span><span class="pun">:</span><span class="pln"> </span><span class="str">'700'</span><span class="pln">
                  </span><span class="pun">}}&gt;</span><span class="pln">
        </span><span class="typ">Big</span><span class="pln"> blue text
      </span><span class="pun">&lt;/</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	بالإضافة إلى أسماء الخصائص، ستلاحظ اختلافًا آخر في المثال السابق. إذ ترتبط بخصائص CSS التي تقبل قيمًا عددية واحدات مثل px أو % أو em أو rem. بينما لا تمتلك الخصائص التي تتعلق بالأبعاد مثل <code>width</code> و<code>height</code> و<code>padding</code> وكذلك <code>font-size</code> في React Native أية واحدات. تمثل هذه القيم العددية بيكسلات مستقلة عن الكثافة density-independent pixels. ولو تساءلت عن خصائص التنسيق المتاحة لمكوّن بنيوي محدد، ستجد الجواب في أوراق التنسيق الزائفة <a href="https://github.com/vhpoet/react-native-styling-cheat-sheet" rel="external nofollow">Styling Cheat Sheet</a> لإطار عمل React Native.
</p>

<p>
	وبشكل عام، لا يعتبر تنسيق المكوًنات باستخدام الخاصية <code>style</code> مباشرة فكرة جيدة، لأنها ستجعل شيفرة المكونات غير واضحة. ويفضّل بدلًا عن ذلك، تعريف التنسيق خارج دالة المكوّن باستخدام التابع <a href="https://reactnative.dev/docs/stylesheet#create" rel="external nofollow">StyleSheet.create</a>. يقبل هذا التابع وسيطًا واحدًا على شكل كائن يتألف بحد ذاته من كائنات تنسيق مسماة ومحددة القيم، كما ينشئ مرجعًا للمكوّن البنيوي <code>StyleSheet</code> من هذا الكائن. سنستعرض فيما يلي طريقة إعادة كتابة المثال السابق باستخدام التابع StyleSheet.create:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_24" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Text</span><span class="pun">,</span><span class="pln"> </span><span class="typ">View</span><span class="pun">,</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> styles </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">.</span><span class="pln">create</span><span class="pun">({</span><span class="pln">
    container</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">20</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    text</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        color</span><span class="pun">:</span><span class="pln"> </span><span class="str">'blue'</span><span class="pun">,</span><span class="pln">    
        fontSize</span><span class="pun">:</span><span class="pln"> </span><span class="lit">24</span><span class="pun">,</span><span class="pln">    
        fontWeight</span><span class="pun">:</span><span class="pln"> </span><span class="str">'700'</span><span class="pun">,</span><span class="pln">  
    </span><span class="pun">},});</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">BigBlueText</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">container</span><span class="pun">}&gt;</span><span class="pln">      
      </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">text</span><span class="pun">}&gt;</span><span class="pln">        
          </span><span class="typ">Big</span><span class="pln"> blue text
      </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	أنشأنا في الشيفرة السابقة كائني تنسيق مسمّيين هما: <code>styles.container</code> و<code>styles.text</code>. يمكننا الوصول ضمن مكوّن إلى كائن تنسيق محدد بنفس الطريقة التي نصل بها إلى كائن صرف.
</p>

<p>
	تقبل الخاصية <code>style</code> مصفوفة من الكائنات بالإضافة إلى الكائنات المفردة. وتتالى كائنات التنسيق في المصفوفة من اليسار إلى اليمين، وبالتالي سيكون للتنسيقات التي تظهر لاحقًا أولوية التنفيذ. كما يمكن تطبيق ذلك بشكل عودي recursive، إذ يمكن على سبيل المثال، أن تحتوي مصفوفة على مصفوفة أخرى لكائنات التنسيق وهكذا. إن كانت إحدى قيم المصفوفة غير صالحة أو كانت "null" أو "undefined"، سيتم تجاهل هذه القيم. وهكذا يصبح من السهل تعريف تنسيقات شرطية كتلك التي تُبنى على أساس قيمة خاصية مثلًا، وإليكم مثالًا عن ذلك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_26" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Text</span><span class="pun">,</span><span class="pln"> </span><span class="typ">View</span><span class="pun">,</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> styles </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">.</span><span class="pln">create</span><span class="pun">({</span><span class="pln">
  text</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    color</span><span class="pun">:</span><span class="pln"> </span><span class="str">'grey'</span><span class="pun">,</span><span class="pln">
    fontSize</span><span class="pun">:</span><span class="pln"> </span><span class="lit">14</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  blueText</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    color</span><span class="pun">:</span><span class="pln"> </span><span class="str">'blue'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  bigText</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fontSize</span><span class="pun">:</span><span class="pln"> </span><span class="lit">24</span><span class="pun">,</span><span class="pln">
    fontWeight</span><span class="pun">:</span><span class="pln"> </span><span class="str">'700'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">FancyText</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> isBlue</span><span class="pun">,</span><span class="pln"> isBig</span><span class="pun">,</span><span class="pln"> children </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> textStyles </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    styles</span><span class="pun">.</span><span class="pln">text</span><span class="pun">,</span><span class="pln">
    isBlue </span><span class="pun">&amp;&amp;</span><span class="pln"> styles</span><span class="pun">.</span><span class="pln">blueText</span><span class="pun">,</span><span class="pln">
    isBig </span><span class="pun">&amp;&amp;</span><span class="pln"> styles</span><span class="pun">.</span><span class="pln">bigText</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">];</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">textStyles</span><span class="pun">}&gt;{</span><span class="pln">children</span><span class="pun">}&lt;/</span><span class="typ">Text</span><span class="pun">&gt;;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Main</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">FancyText</span><span class="pun">&gt;</span><span class="typ">Simple</span><span class="pln"> text</span><span class="pun">&lt;/</span><span class="typ">FancyText</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">FancyText</span><span class="pln"> isBlue</span><span class="pun">&gt;</span><span class="typ">Blue</span><span class="pln"> text</span><span class="pun">&lt;/</span><span class="typ">FancyText</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">FancyText</span><span class="pln"> isBig</span><span class="pun">&gt;</span><span class="typ">Big</span><span class="pln"> text</span><span class="pun">&lt;/</span><span class="typ">FancyText</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">FancyText</span><span class="pln"> isBig isBlue</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="typ">Big</span><span class="pln"> blue text
      </span><span class="pun">&lt;/</span><span class="typ">FancyText</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	استخدمنا في الشيفرة السابقة العامل &amp;&amp; في العبارة <code>condition &amp;&amp; exprIfTrue</code>. سيعطي تنفيذ هذه العبارة قيمة "experIfTrue" إن كانت قيمة "condition" هي "true"، وإلا سيعطي القيمة "condition" والتي ستأخذ في هذه الحالة القيمة "false". هذه الطريقة المختصرة شائعة جدًا ومفيدة جدًا. كما يمكنك استخدام <a href="https://wiki.hsoub.com/JavaScript/Conditional_Operator" rel="external">العامل الشرطي الثلاثي</a> ":?" كالتالي: <code>condition ? exprIfTrue : exprIfFalse</code>
</p>

<h2>
	سمات بواجهة مستخدم متناسقة
</h2>

<p>
	لنبقى في مجال تنسيق التطبيقات لكن من منظورٍ أوسع قليلًا. لقد استخدم معظمنا تطبيقات متنوعة، وربما سنتفق أن ما بجعل واجهة المستخدم جيدة هو تناسق العناصر. فمظهر مكوّنات واجهة المستخدم كحجم الخط وعائلته ولونه ستتبع أسلوبًا متناسقًا. ولإنجاز ذلك لابد من ربط قيم خصائص التنسيق المختلفة بمعاملات، وهذا ما يعرف ببناء السمات theming.
</p>

<p>
	ربما يكون مصطلح "بناء السمات" مألوفًا لمستخدمي مكتبات بناء واجهات المستخدم المشهورة مثل <a href="https://getbootstrap.com/docs/4.4/getting-started/theming/" rel="external nofollow">Bootstrap</a> و<a href="https://material-ui.com/customization/theming/" rel="external nofollow">Material UI</a>. وعلى الرغم من اختلاف طرق بناء السمات، إلا أنّ الفكرة الرئيسية ستبقى دائمًا استخدام المتغيرات مثل <code>colors.primary</code> بدلًا من <a href="https://en.wikipedia.org/wiki/Magic_number_(programming)" rel="external nofollow">الأرقام السحرية</a> مثل 0366dd# عند تعريف التنسيق، وهذا ما سيقود إلى زيادة في الاتساق والمرونة.
</p>

<p>
	دعونا نلقي نظرة على طريقة التنفيذ العملي لبناء السمات في تطبيقنا. سنستخدم في عملنا العديد من النصوص وبتنسيقات مختلفة مثل حجم الخط ولونه. وطالما أنّ React Native لا تدعم التنسيق العام، لا بد من إنشاء مكوّنات <code>Text</code> خاصة بنا لنبقي المحتوى النصي متسقًا. لنبدأ إذًا بإضافة شيفرة كائن "تهيئة التنسيق" التالية إلى الملف "theme.js" الموجود في المجلد "src":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_28" style="">
<span class="kwd">const</span><span class="pln"> theme </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  colors</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    textPrimary</span><span class="pun">:</span><span class="pln"> </span><span class="str">'#24292e'</span><span class="pun">,</span><span class="pln">
    textSecondary</span><span class="pun">:</span><span class="pln"> </span><span class="str">'#586069'</span><span class="pun">,</span><span class="pln">
    primary</span><span class="pun">:</span><span class="pln"> </span><span class="str">'#0366d6'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  fontSizes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    body</span><span class="pun">:</span><span class="pln"> </span><span class="lit">14</span><span class="pun">,</span><span class="pln">
    subheading</span><span class="pun">:</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  fonts</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    main</span><span class="pun">:</span><span class="pln"> </span><span class="str">'System'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  fontWeights</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    normal</span><span class="pun">:</span><span class="pln"> </span><span class="str">'400'</span><span class="pun">,</span><span class="pln">
    bold</span><span class="pun">:</span><span class="pln"> </span><span class="str">'700'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> theme</span><span class="pun">;</span></pre>

<p>
	علينا الآن إنشاء الكائن <code>Text</code> الفعلي الذي يستخدم قواعد تهيئة التنسيق. لننشئ إذًا الملف "Text.jsx" في المجلد "components" الذي يحتوي ملفات بقية المكوّنات، ولنضع فيه الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_30" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Text</span><span class="pln"> as </span><span class="typ">NativeText</span><span class="pun">,</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> theme from </span><span class="str">'../theme'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> styles </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">.</span><span class="pln">create</span><span class="pun">({</span><span class="pln">
  text</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    color</span><span class="pun">:</span><span class="pln"> theme</span><span class="pun">.</span><span class="pln">colors</span><span class="pun">.</span><span class="pln">textPrimary</span><span class="pun">,</span><span class="pln">
    fontSize</span><span class="pun">:</span><span class="pln"> theme</span><span class="pun">.</span><span class="pln">fontSizes</span><span class="pun">.</span><span class="pln">body</span><span class="pun">,</span><span class="pln">
    fontFamily</span><span class="pun">:</span><span class="pln"> theme</span><span class="pun">.</span><span class="pln">fonts</span><span class="pun">.</span><span class="pln">main</span><span class="pun">,</span><span class="pln">
    fontWeight</span><span class="pun">:</span><span class="pln"> theme</span><span class="pun">.</span><span class="pln">fontWeights</span><span class="pun">.</span><span class="pln">normal</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  colorTextSecondary</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    color</span><span class="pun">:</span><span class="pln"> theme</span><span class="pun">.</span><span class="pln">colors</span><span class="pun">.</span><span class="pln">textSecondary</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  colorPrimary</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    color</span><span class="pun">:</span><span class="pln"> theme</span><span class="pun">.</span><span class="pln">colors</span><span class="pun">.</span><span class="pln">primary</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  fontSizeSubheading</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fontSize</span><span class="pun">:</span><span class="pln"> theme</span><span class="pun">.</span><span class="pln">fontSizes</span><span class="pun">.</span><span class="pln">subheading</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  fontWeightBold</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fontWeight</span><span class="pun">:</span><span class="pln"> theme</span><span class="pun">.</span><span class="pln">fontWeights</span><span class="pun">.</span><span class="pln">bold</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Text</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> color</span><span class="pun">,</span><span class="pln"> fontSize</span><span class="pun">,</span><span class="pln"> fontWeight</span><span class="pun">,</span><span class="pln"> style</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln">props </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> textStyle </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    styles</span><span class="pun">.</span><span class="pln">text</span><span class="pun">,</span><span class="pln">
    color </span><span class="pun">===</span><span class="pln"> </span><span class="str">'textSecondary'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> styles</span><span class="pun">.</span><span class="pln">colorTextSecondary</span><span class="pun">,</span><span class="pln">
    color </span><span class="pun">===</span><span class="pln"> </span><span class="str">'primary'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> styles</span><span class="pun">.</span><span class="pln">colorPrimary</span><span class="pun">,</span><span class="pln">
    fontSize </span><span class="pun">===</span><span class="pln"> </span><span class="str">'subheading'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> styles</span><span class="pun">.</span><span class="pln">fontSizeSubheading</span><span class="pun">,</span><span class="pln">
    fontWeight </span><span class="pun">===</span><span class="pln"> </span><span class="str">'bold'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> styles</span><span class="pun">.</span><span class="pln">fontWeightBold</span><span class="pun">,</span><span class="pln">
    style</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">];</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">NativeText</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">textStyle</span><span class="pun">}</span><span class="pln"> </span><span class="pun">{...</span><span class="pln">props</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">Text</span><span class="pun">;</span></pre>

<p>
	وهكذا نكون قد أنجزنا مكوّن النص الخاص بنا من حيث اللون وحجم الخط وكثافته لنستخدمه في أي مكان من تطبيقنا. يمكننا الحصول على تغييرات مختلفة في هذا المكوّن باستخدام خصائص مختلفة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_33" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Text</span><span class="pln"> from </span><span class="str">'./Text'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Main</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="typ">Simple</span><span class="pln"> text</span><span class="pun">&lt;/</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pln"> style</span><span class="pun">={{</span><span class="pln"> paddingBottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="pun">}}&gt;</span><span class="typ">Text</span><span class="pln"> </span><span class="kwd">with</span><span class="pln"> custom style</span><span class="pun">&lt;/</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pln"> fontWeight</span><span class="pun">=</span><span class="str">"bold"</span><span class="pln"> fontSize</span><span class="pun">=</span><span class="str">"subheading"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="typ">Bold</span><span class="pln"> subheading
      </span><span class="pun">&lt;/</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pln"> color</span><span class="pun">=</span><span class="str">"textSecondary"</span><span class="pun">&gt;</span><span class="typ">Text</span><span class="pln"> </span><span class="kwd">with</span><span class="pln"> secondary color</span><span class="pun">&lt;/</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">Main</span><span class="pun">;</span></pre>

<p>
	لك كامل الحرية في توسيع أو تعديل هذا المكوّن إن أردت. وقد يكون إنشاء مكوّنات نصية قابلة لإعادة الاستخدام تستخدم المكوّن <code>Text</code> مثل <code>Subheading</code> فكرة جيدة. حاول أيضًا أن توسع أو تعدل قواعد تهيئة السمة كلما تقدمنا في بناء تطبيقنا.
</p>

<h2>
	استخدام flexbox في تصميم واجهة التطبيق
</h2>

<p>
	سنعرّج أخيرًا في عرضنا لمواضيع تنسيق التطبيقات على تصميم الواجهات باستخدام <a href="https://wiki.hsoub.com/CSS#.D8.AA.D8.AE.D8.B7.D9.8A.D8.B7_Flex_Box" rel="external">flexbox</a>. يعرف المطورون الذين ألفوا اللغة CSS أنّ flexbox لا يتعلق فقط بإطار العمل React Native، فقد يستعمل في حالات عدة لتطوير صفحات الويب أيضًا. وعمليًا لن يتعلم المطورون الذين يعرفون آلية عمل flexbox الكثير من هذه الفقرة، لكن دعونا على الأقل نراجع أو نتعلم أساسيات flexbox.
</p>

<p>
	flexbox هو كيان لتصميم الواجهات يتألف من مكونين منفصلين هما: حاوية flex وضمنها مجموعة من عناصر flex. تمتلك حاوية flex مجموعة من الخصائص التي تتحكم بتوزيع العناصر. ولجعل مكوّن ما حاوية flex، لا بد من أن يمتلك خاصية التنسيق <code>display</code> وقد أسندت لها القيمة "flex" وهي القيمة الافتراضية لهذه الخاصية. تظهر الشيفرة التالية طريقة تعريف حاوية flex:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_35" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">View</span><span class="pun">,</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> styles </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">.</span><span class="pln">create</span><span class="pun">({</span><span class="pln">
  flexContainer</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    flexDirection</span><span class="pun">:</span><span class="pln"> </span><span class="str">'row'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">FlexboxExample</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">flexContainer</span><span class="pun">}&gt;{</span><span class="com">/* ... */</span><span class="pun">}&lt;/</span><span class="typ">View</span><span class="pun">&gt;;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	ربما تكون الخصائص التالية أهم خصائص حاوية flex:
</p>

<ul>
<li>
		<p>
			الخاصية <a href="https://css-tricks.com/almanac/properties/f/flex-direction/" rel="external nofollow">flexDirection</a>: وتتحكم بالاتجاه الذي ستأخذه عناصر flex عند ترتيبها ضمن الحاوية. وتأخذ هذه الخاصية القيم row وrow-reverse وcoulmn وcolumn-reverse. سيرتب row عناصر flex من اليسار إلى اليمين، بينما سيرتبها column من الأعلى للأسفل (القيمة الافتراضية). وستعكس القيم ذات اللاحقة reverse- اتجاه ترتيب العناصر.
		</p>
	</li>
	<li>
		<p>
			الخاصية <a href="https://css-tricks.com/almanac/properties/j/justify-content/" rel="external nofollow">justifyContent</a>: وتتحكم بمحاذاة عناصر flex على طول المحور الرئيسي للترتيب الذي تحدده الخاصية السابقة. تأخذ هذه الخاصية القيم flex-start (القيمة الافتراضية) وflex-end وcenter وspace-between وspace-around وspace-evenly.
		</p>
	</li>
	<li>
		<p>
			الخاصية <a href="https://css-tricks.com/almanac/properties/a/align-items/" rel="external nofollow">alignItems</a>: وتقوم بوظيفة الخاصية السابقة لكن على المحور الآخر لترتيب العناصر. تأخذ هذه الخاصية القيم flex-start وflex-end وcenter وbaseline و stretch (القيمة الافتراضية).
		</p>
	</li>
</ul>
<p>
	لننتقل الآن إلى عناصر flex والتي أشرنا سابقًا على أنها محتواة ضمن حاوية flex. تمتلك هذه العناصر خصائص تتحكم بتنسيق وسلوك هذه العناصر بالنسبة لبعضها في نفس الحاوية. ولكي يكون المكوّن عنصر flex يكفي أن نجعله ابنا مباشرًا لحاوية flex:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_37" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">View</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Text</span><span class="pun">,</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> styles </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">.</span><span class="pln">create</span><span class="pun">({</span><span class="pln">
  flexContainer</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    display</span><span class="pun">:</span><span class="pln"> </span><span class="str">'flex'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  flexItemA</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    flexGrow</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
    backgroundColor</span><span class="pun">:</span><span class="pln"> </span><span class="str">'green'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  flexItemB</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    flexGrow</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
    backgroundColor</span><span class="pun">:</span><span class="pln"> </span><span class="str">'blue'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">FlexboxExample</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">flexContainer</span><span class="pun">}&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">flexItemA</span><span class="pun">}&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="typ">Flex</span><span class="pln"> item A</span><span class="pun">&lt;/</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">flexItemB</span><span class="pun">}&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="typ">Flex</span><span class="pln"> item B</span><span class="pun">&lt;/</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	إنّ أكثر خصائص العناصر استخدامًا هي <a href="https://wiki.hsoub.com/CSS/flex-grow/" rel="external">flexGrow</a>. حيث تقبل هذه الخاصية قيمة تحدد قدرة العنصر على النمو إن دعت الحاجة. فلو كانت قيمة هذه الخاصية لكل عناصر flex في حاوية هي 1، ستتقاسم هذه العناصر المساحة المتاحة للحاوية بالتساوي. بينما لو كانت قيمتها 0 لعنصر ما، فسيأخذ المساحة التي يتطلبها محتواه فقط، وسيترك المساحة الباقية لبقية العناصر.
</p>

<p>
	ننصحك بدراسة المثال النموذجي <a href="https://snack.expo.io/@kalleilv/3d045d" rel="external nofollow">Flexbox example</a>، والذي يشكّل مثالًا رئيسًا أكثر تفاعلية لاستخدام flexbox في تنفيذ بطاقة بسيطة لها حاشية علوية وجسم وحاشية سفلية.
</p>

<p>
	إقرأ تاليًا المقالة <a href="https://academy.hsoub.com/programming/css/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-css-flexbox-%D9%88%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84%D9%87-%D9%84%D9%87%D9%8A%D9%83%D9%84%D8%A9-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r118/" rel="">تعرف على CSS Flexbox وأساسيات استعماله لهيكلة صفحات الويب</a> التي تحتوي على أمثلة مصوّرة شاملة عن استخدام flex. ومن الجيد أيضًا أن تجرب خصائص flexbox ضمن أرضية العمل <a href="https://demos.scotch.io/visual-guide-to-css3-flexbox-flexbox-playground/demos/" rel="external nofollow">Flexbox Playground</a> لترى كيفية تأثير الخصائص على تصميم الواجهة. وتذكر أنّ أسماء الخصائص في React Native هي نفسها في CSS ما عدا نقطة التسمية بأسلوب سنام الجمل، إلا أن قيم هذه الخاصيات مثل flex-start وflex-end ستبقى نفسها.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>ملاحظة</strong>: تختلف React Native عن CSS في بعض النواحي المتعلقة باستخدام flexbox. فالإختلاف الأهم هو أنّ القيمة الافتراضية للخاصية <code>flexDirection</code> في React Native هي column. كما يجدر الإشارة إلى أنّ اختصارات flex لا تقبل قيمًا متعددة في React Native. لتطلع أكثر على استخدام flexbox في تطبيقات React Native راجع <a href="https://reactnative.dev/docs/flexbox" rel="external nofollow">توثيق React Native</a>.
	</p>
</blockquote>

<h2>
	التمارين 10.4 - 10.5
</h2>

<h3>
	10.4 شريط تنقل للتطبيق
</h3>

<p>
	سنحتاج قريبًا إلى التنقل بين الواجهات المختلفة للتطبيق، لذلك سنحتاج إلى <a href="https://material.io/components/app-bars-top/" rel="external nofollow">شريط تنقل</a> بين هذه الواجهات. أنشئ الملف "AppBar.jsx" وضعه في المجلد "components" ثم ضع الشيفرة التالية ضمنه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_40" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">View</span><span class="pun">,</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Constants</span><span class="pln"> from </span><span class="str">'expo-constants'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> styles </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">.</span><span class="pln">create</span><span class="pun">({</span><span class="pln">
  container</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    paddingTop</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Constants</span><span class="pun">.</span><span class="pln">statusBarHeight</span><span class="pun">,</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">AppBar</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">container</span><span class="pun">}&gt;{</span><span class="com">/* ... */</span><span class="pun">}&lt;/</span><span class="typ">View</span><span class="pun">&gt;;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">AppBar</span><span class="pun">;</span></pre>

<p>
	وبما أنّ المكوّن <code>AppBar</code> سيمنع شريط الحالة من تغطية المحتوى، يمكن إزالة خاصية التنسيق <code>marginTop</code> التي وضعناها للمكوّن<code>Main</code> في الملف "Main.jsx". ينبغي أن يضم المكوّن <code>AppBar</code> نافذة عنوانها "Repositories". اجعل النافذة مستجيبة للمس touchable باستخدام المكوّن <a href="https://reactnative.dev/docs/touchablewithoutfeedback" rel="external nofollow">TouchableWithoutFeedback</a>، لكن لا حاجة الآن لتعالج الحدث <code>onPress</code>. أضف المكوّن <code>AppBar</code> إلى المكوّن <code>Main</code> ، بحيث يبقى المكوّن الأعلى في الواجهة. من المفترض أن يبدو المكوّن <code>AppBar</code> شبيهًا للتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="71742" href="https://academy.hsoub.com/uploads/monthly_2021_07/appbar_component_02.png.a1006b5b2e899072886a7fd4d0a2edc9.png" rel=""><img alt="appbar_component_02.png" class="ipsImage ipsImage_thumbnailed" data-fileid="71742" data-unique="rp4fxml00" src="https://academy.hsoub.com/uploads/monthly_2021_07/appbar_component_02.thumb.png.1e47ae038042f06b1052ff4f9f0413f0.png"></a>
</p>

<p>
	إن لون خلفية التطبيق كما تظهره الصورة السابقة هو 24292e#، لكن يمكنك استخدام اللون الذي تشاء. يمكنك أيضًا تهيئة لون خلفية التطبيق ضمن قواعد تهيئة السمة، وسيصبح تغيير اللون أمرًا سهلًا إن احتجت لذلك. ومن الجيد أيضًا فصل شيفرة النافذة الموجودة في المكوّن <code>AppBar</code> لتصبح ضمن مكوّن مستقل يمكن أن تسميه <code>AppBarTab</code>، سيسهّل ذلك إضافة نوافذ أخرى مستقبلًا.
</p>

<h3>
	10.5 قائمة محسنة المظهر للمستودعات المقيمة
</h3>

<p>
	تبدو النسخة الحالية من تطبيق قائمة المستودعات المقيَّمة كئيبة المظهر. عدّل المكوّن <code>RepositoryListItem</code> ليعرض أيضًا الصورة التمثيلية لمؤلف المستودع. ويمكنك إنجاز ذلك باستخدام المكوّن <a href="https://wiki.hsoub.com/ReactNative/image" rel="external">Image</a>. ينبغي إظهار القيم العددية كعدد النجمات وعدد التشعبات التي تزيد عن 1000 برقم مرتبة الآلاف وبدقة فاصلة عشرية واحدة فقط تليها اللاحقة "k". فلو كان مثلًا عدد التشعبات هو 8439، ستُعرض على الشاشة القيمة "8.4k". حسِّن أيضًا المظهر الكلي للمكوّن بحيث تبدو قائمة المستودعات مشابهة للتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="71743" href="https://academy.hsoub.com/uploads/monthly_2021_07/polished_repo_list_03.png.fb6f527ef60e4625381b32dc83333317.png" rel=""><img alt="polished_repo_list_03.png" class="ipsImage ipsImage_thumbnailed" data-fileid="71743" data-unique="vzkw15kdm" src="https://academy.hsoub.com/uploads/monthly_2021_07/polished_repo_list_03.thumb.png.06ebbfc7757c62995f428070a214e73f.png"></a>
</p>

<p>
	إنّ لون الخلفية للمكوّن <code>Main</code> في الصورة السابقة هو e1e4e8# بينما ستظهر خلفية للمكوّن <code>RepositoryListItem</code> باللون الأبيض. كما يأخذ لون خلفية محدد اللغة القيمة 0366d6 #، وهي نفسها قيمة المتغيّر <code>colors.primary</code> في إعدادات تهيئة السمة. لا تنس الاستفادة من المكوّن <code>Text</code> الذي أنجزناه سابقًا. افصل المكوّن <code>RepositoryListItem</code> -إن دعت الحاجة- إلى مكوّنات أصغر.
</p>

<h2>
	مسارات التنقل في React Native
</h2>

<p>
	سرعان ما سنحتاج إلى آلية للانتقال بين مختلف واجهات العرض في التطبيق كواجهة المستودعات وواجهة تسجيل الدخول، وذلك عندما نبدأ بتوسيعه. وكنا قد تعلمنا في القسم 7 استخدام المكتبة <a href="https://reacttraining.com/react-router/native/guides/quick-start" rel="external nofollow">React router</a> لتنفيذ مهمة التنقل والتوجه في تطبيقات الويب.
</p>

<p>
	تختلف آلية التنقل في React Native قليلًا عن التنقل في تطبيقات الويب. ويظهر الاختلاف الرئيسي في عدم القدرة على الإشارة المرجعية للصفحات من خلال العناوين "URL" التي نكتبها في شريط تنقل المتصفح، ولا يمكننا كذلك التنقل بين الصفحات التالية والسابقة اعتمادًا على الواجهة البرمجية لتاريخ تنقلات المستخدم <a href="https://developer.mozilla.org/en-US/docs/Web/API/History_API" rel="external nofollow">history <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a> في المتصفح. لكن تبقى هذه الإشكالية مسألة تتعلق بواجهة التنقل التي نستخدمها.
</p>

<p>
	يمكننا في ReactNative استخدام متحكمات المسار البنيوية React Router's core بالكامل كالخطافات والمكوّنات. لكن علينا استبدال <code>BrowserRouter</code> الذي يستخدم في بيئة المتصفح بمقابله <a href="https://reacttraining.com/react-router/native/api/NativeRouter" rel="external nofollow">NativeRouter</a> الملائم لإطار عمل React Native والذي تؤمنه المكتبة <a href="https://reacttraining.com/react-router/native/guides/quick-start" rel="external nofollow">react-router-native</a>. لنثبّت إذًا هذه المكتبة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_42" style="">
<span class="pln">npm install react</span><span class="pun">-</span><span class="pln">router</span><span class="pun">-</span><span class="pln">native</span></pre>

<p>
	سيؤدي استخدام المكتبة react-router-native إلى توقف عرض التطبيق ضمن متصفح ويب المنصة Expo، بينما ستعمل بقية طرق عرض التطبيق بشكل جيد. يمكن إصلاح هذه المشكلة بتوسيع إعدادات تهيئة المجمع <a href="%5Bhttps://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-webpack-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-r866/%5D(https://www.google.com/url?q=https://academy.hsoub.com/programming/workflow/%D8%AF%D9%84%D9%8A%D9%84-webpack-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-r866/&amp;sa=D&amp;ust=1611853784976000&amp;usg=AOvVaw33z8m5FGdt9_X3gl3zZmXH)" rel="">webpack</a> للمنصة Expo بحيث ينقل transpile الشيفرة المصدرية للمكتبة باستخدام Babel. ولتوسيع إعدادات التهيئة، لابدّ من تثبيت المكتبة expo/webpack-config@:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_46" style="">
<span class="pln">npm install </span><span class="lit">@expo</span><span class="pun">/</span><span class="pln">webpack</span><span class="pun">-</span><span class="pln">config </span><span class="pun">--</span><span class="pln">save</span><span class="pun">-</span><span class="pln">dev</span></pre>

<p>
	سننشئ بعد ذلك الملف "webpack.config.js" في المجلد الجذري للمشروع وضمنه الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_44" style="">
<span class="kwd">const</span><span class="pln"> path </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'path'</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> createExpoWebpackConfigAsync </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'@expo/webpack-config'</span><span class="pun">);</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> async </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">env</span><span class="pun">,</span><span class="pln"> argv</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> config </span><span class="pun">=</span><span class="pln"> await createExpoWebpackConfigAsync</span><span class="pun">(</span><span class="pln">env</span><span class="pun">,</span><span class="pln"> argv</span><span class="pun">);</span><span class="pln">

  config</span><span class="pun">.</span><span class="pln">module</span><span class="pun">.</span><span class="pln">rules</span><span class="pun">.</span><span class="pln">push</span><span class="pun">({</span><span class="pln">
    test</span><span class="pun">:</span><span class="pln"> </span><span class="str">/\.js$/</span><span class="pun">,</span><span class="pln">
    loader</span><span class="pun">:</span><span class="pln"> </span><span class="str">'babel-loader'</span><span class="pun">,</span><span class="pln">
    include</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">path</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> </span><span class="str">'node_modules/react-router-native'</span><span class="pun">)],</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> config</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	أعد تشغيل أدوات تطوير لكي تُطبق إعدادات تهيئة webpack الجديدة، وستجد أن المشكلة قد حُلّت. افتح الآن الملف "App.js" وأضف المكوّن <code>NativeRouter</code> إلى المكوّن <code>App</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_48" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">NativeRouter</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-router-native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Main</span><span class="pln"> from </span><span class="str">'./src/components/Main'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">NativeRouter</span><span class="pun">&gt;</span><span class="pln">
     </span><span class="pun">&lt;</span><span class="typ">Main</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">NativeRouter</span><span class="pun">&gt;</span><span class="pln">  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span><span class="pun">;</span></pre>

<p>
	بعد أن أنشأنا متحكمًا بالمسار، سنضيف أول مسار تنقل (وجهة- Route) في المكوّن <code>Main</code>، وذلك ضمن الملف "Main.jsx":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_50" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">,</span><span class="pln"> </span><span class="typ">View</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Route</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Switch</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Redirect</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-router-native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">RepositoryList</span><span class="pln"> from </span><span class="str">'./RepositoryList'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">AppBar</span><span class="pln"> from </span><span class="str">'./AppBar'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> theme from </span><span class="str">'../theme'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> styles </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">.</span><span class="pln">create</span><span class="pun">({</span><span class="pln">
  container</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    backgroundColor</span><span class="pun">:</span><span class="pln"> theme</span><span class="pun">.</span><span class="pln">colors</span><span class="pun">.</span><span class="pln">mainBackground</span><span class="pun">,</span><span class="pln">
    flexGrow</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
    flexShrink</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Main</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">container</span><span class="pun">}&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">AppBar</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">Switch</span><span class="pun">&gt;</span><span class="pln">        
      </span><span class="pun">&lt;</span><span class="typ">Route</span><span class="pln"> path</span><span class="pun">=</span><span class="str">"/"</span><span class="pln"> exact</span><span class="pun">&gt;</span><span class="pln">          
          </span><span class="pun">&lt;</span><span class="typ">RepositoryList</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">        
          </span><span class="pun">&lt;/</span><span class="typ">Route</span><span class="pun">&gt;</span><span class="pln">        
      </span><span class="pun">&lt;</span><span class="typ">Redirect</span><span class="pln"> to</span><span class="pun">=</span><span class="str">"/"</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">      
     </span><span class="pun">&lt;/</span><span class="typ">Switch</span><span class="pun">&gt;</span><span class="pln">    
   </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">Main</span><span class="pun">;</span></pre>

<h2>
	التمرينان 10.6 - 10.7
</h2>

<h3>
	10.6 واجهة عرض تسجيل الدخول
</h3>

<p>
	سننفذ قريبًا نموذجًا لكي يصبح المستخدم قادرًا على تسجيل دخوله إلى التطبيق. لكن علينا قبل ذلك أن ننشئ واجهة عرض يمكن الوصول إليها من شريط التطبيق. أنشئ الملف "Signin.jsx" في المجلد "components"، ثم ضع الشيفرة التالية ضمنه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_52" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Text</span><span class="pln"> from </span><span class="str">'./Text'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">SignIn</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="typ">The</span><span class="pln"> sign in view</span><span class="pun">&lt;/</span><span class="typ">Text</span><span class="pun">&gt;;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">SignIn</span><span class="pun">;</span></pre>

<p>
	هيئ مسارًا وجهته المكوّن <code>Signin</code> وضعه في المكوًن <code>Main</code>. أضف بعد ذلك نافذة عنوانها النص "Sign in" في أعلى شريط التطبيق إلى جوار النافذة "Repositories". ينبغي أن يكون المستخدم قادرًا على التنقل بين النافذتين السابقتين بالضغط على عنوان كل نافذة
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		تلميح: استخدم المكوّن <a href="https://reacttraining.com/react-router/native/api/Link" rel="external nofollow">Link</a> والخاصية <a href="https://reacttraining.com/react-router/native/api/Link/component-func" rel="external nofollow">component</a> العائدة له.
	</p>
</blockquote>

<h3>
	10.7 شريط تطبيق قابل للتمرير scrollable
</h3>

<p>
	طالما أننا سنضيف نوافذ جديدة إلى شريط التطبيق، فمن الجيد أن يكون قابلًا للتمرير أفقيًا عندما لا تتسع الواجهة لكل النوافذ. سنستخدم في ذلك المكوّن الأنسب لأداء المهمة وهو <a href="https://wiki.hsoub.com/ReactNative/scrollview" rel="external">ScrollView</a>. ضع النوافذ الموجودة في المكوّن <code>AppBar</code> ضمن المكوّن <code>ScrollView</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_54" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">AppBar</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">container</span><span class="pun">}&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">ScrollView</span><span class="pln"> horizontal</span><span class="pun">&gt;{</span><span class="com">/* ... */</span><span class="pun">}&lt;</span><span class="str">/ScrollView&gt;    &lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	عندما تُسند القيمة "true" إلى الخاصية <a href="https://wiki.hsoub.com/ReactNative/scrollview#horizontal" rel="external">horizontal</a> العائدة للمكون <code>ScrollView</code>، سيجعله قابلًا للتمرير أفقيًا عندما لا تتسع الشاشة للنوافذ التي يحتويها شريط التطبيق. وانتبه إلى ضرورة إضافة خصائص تنسيق ملائمة للمكوّن <code>ScrollView</code>، بحيث تُرتَّب النوافذ على شكل صف "row" ضمن حاوية flex. للتأكد من إنجاز المطلوب أضف نوافذ جديدة إلى الشريط حتى لا تتسع الشاشة لها جميعًا، ولا تنس إزالة هذه النوافذ التجريبية بعد التحقق من إنجاز المهمة.
</p>

<h2>
	إدارة حالة النموذج
</h2>

<p>
	بعد أن حصلنا على حاضنة مناسبة لواجهة عرض تسجيل الدخول، ستكون المهمة التالية إنجاز نموذج لتسجيل الدخول. لكن قبل الشروع في ذلك، سنتحدث قليلًا عن النماذج وبمنظور أوسع.
</p>

<p>
	يعتمد تنفيذ النماذج بشدة على إدارة الحالة. فقد يقدم لنا الخطاف <code>useState</code> حلًا جيدًا للنماذج الصغيرة، لكن سيكون التعامل مع الحالة بنفس الأسلوب مرهقًا عندما يغدو النموذج أكثر تعقيدًا. ولحسن الحظ سنجد مكتبات تتعايش مع بيئة React لتسهيل عملية إدارة حالة النماذج ومنها المكتبة <a href="https://jaredpalmer.com/formik/" rel="external nofollow">Formik</a>.
</p>

<p>
	تعتمد المكتبة Formik على مفهومي سياق العمل context والحقل field. يؤمن سياق العمل المكوّن <a href="https://jaredpalmer.com/formik/docs/api/formik" rel="external nofollow">Formik</a> الذي يحتوي على حالة النموذج. وتتكون الحالة من معلومات عن حقول النموذج، وتتضمن هذه المعلومات مثلًا قيم الحقول والأخطاء الناتجة عن تقييم كلٍ منها. يمكن الإشارة إلى حالة الحقول باستخدام اسم الحقل عن طريق الخطاف <a href="https://jaredpalmer.com/formik/docs/api/useField" rel="external nofollow">useField</a> أو المكوّن <a href="https://jaredpalmer.com/formik/docs/api/field" rel="external nofollow">Field</a>.
</p>

<p>
	لنرى كيف سيعمل الأمر من خلال إنشاء نموذج لحساب "<a href="https://en.wikipedia.org/wiki/Body_mass_index" rel="external nofollow">مؤشر كتلة الجسم</a>" عند البشر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_57" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Text</span><span class="pun">,</span><span class="pln"> </span><span class="typ">TextInput</span><span class="pun">,</span><span class="pln"> </span><span class="typ">TouchableWithoutFeedback</span><span class="pun">,</span><span class="pln"> </span><span class="typ">View</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Formik</span><span class="pun">,</span><span class="pln"> useField </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'formik'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> initialValues </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  mass</span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pun">,</span><span class="pln">
  height</span><span class="pun">:</span><span class="pln"> </span><span class="str">''</span><span class="pun">,</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> getBodyMassIndex </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">mass</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">round</span><span class="pun">(</span><span class="pln">mass </span><span class="pun">/</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">pow</span><span class="pun">(</span><span class="pln">height</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">));</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">BodyMassIndexForm</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> onSubmit </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">massField</span><span class="pun">,</span><span class="pln"> massMeta</span><span class="pun">,</span><span class="pln"> massHelpers</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useField</span><span class="pun">(</span><span class="str">'mass'</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">heightField</span><span class="pun">,</span><span class="pln"> heightMeta</span><span class="pun">,</span><span class="pln"> heightHelpers</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useField</span><span class="pun">(</span><span class="str">'height'</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">TextInput</span><span class="pln">
        placeholder</span><span class="pun">=</span><span class="str">"Weight (kg)"</span><span class="pln">
        value</span><span class="pun">={</span><span class="pln">massField</span><span class="pun">.</span><span class="pln">value</span><span class="pun">}</span><span class="pln">
        onChangeText</span><span class="pun">={</span><span class="pln">text </span><span class="pun">=&gt;</span><span class="pln"> massHelpers</span><span class="pun">.</span><span class="pln">setValue</span><span class="pun">(</span><span class="pln">text</span><span class="pun">)}</span><span class="pln">
      </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">TextInput</span><span class="pln">
        placeholder</span><span class="pun">=</span><span class="str">"Height (m)"</span><span class="pln">
        value</span><span class="pun">={</span><span class="pln">heightField</span><span class="pun">.</span><span class="pln">value</span><span class="pun">}</span><span class="pln">
        onChangeText</span><span class="pun">={</span><span class="pln">text </span><span class="pun">=&gt;</span><span class="pln"> heightHelpers</span><span class="pun">.</span><span class="pln">setValue</span><span class="pun">(</span><span class="pln">text</span><span class="pun">)}</span><span class="pln">
      </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">TouchableWithoutFeedback</span><span class="pln"> onPress</span><span class="pun">={</span><span class="pln">onSubmit</span><span class="pun">}&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="typ">Calculate</span><span class="pun">&lt;/</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="typ">TouchableWithoutFeedback</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">BodyMassIndexCalculator</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> onSubmit </span><span class="pun">=</span><span class="pln"> values </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> mass </span><span class="pun">=</span><span class="pln"> parseFloat</span><span class="pun">(</span><span class="pln">values</span><span class="pun">.</span><span class="pln">mass</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> height </span><span class="pun">=</span><span class="pln"> parseFloat</span><span class="pun">(</span><span class="pln">values</span><span class="pun">.</span><span class="pln">height</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">isNaN</span><span class="pun">(</span><span class="pln">mass</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">!</span><span class="pln">isNaN</span><span class="pun">(</span><span class="pln">height</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> height </span><span class="pun">!==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Your</span><span class="pln"> body mass index is</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">getBodyMassIndex</span><span class="pun">(</span><span class="pln">mass</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">)}`);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">Formik</span><span class="pln"> initialValues</span><span class="pun">={</span><span class="pln">initialValues</span><span class="pun">}</span><span class="pln"> onSubmit</span><span class="pun">={</span><span class="pln">onSubmit</span><span class="pun">}&gt;</span><span class="pln">
      </span><span class="pun">{({</span><span class="pln"> handleSubmit </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">BodyMassIndexForm</span><span class="pln"> onSubmit</span><span class="pun">={</span><span class="pln">handleSubmit</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;}</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">Formik</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	لا يعتبر هذا المثال جزءًا من تطبيقنا، لذا لا حاجة لإضافة شيفرته إلى التطبيق. لكن يمكنك تجريب الشيفرة في <a href="https://snack.expo.io/" rel="external nofollow">Expo Snack</a> مثلًا، وهو محرر لشيفرة React Native على شبكة الإنترنت على غرار <a href="https://jsfiddle.net/" rel="external nofollow">JSFiddle</a> و<a href="https://codepen.io/" rel="external nofollow">CodePen</a>، ويمثل منصة مفيدة لتجريب الشيفرات بسرعة. كما يمكنك مشاركة Expo Snacks مع الآخرين بتحديد رابط إلى عملك أو تضمين الشيفرة على شكل مشغّل Snack ضمن أي صفحة ويب. ولربما قد صادفت مسبقًا مشغل Snack في مادة منهاجنا أو أثناء اطلاعك على توثيق React Native.
</p>

<p>
	عرّفنا في المثال السابق سياق عمل Formik ضمن المكوّن <code>BodyMassIndexCalculator</code>، وزودناه بالقيم الأولية وباستدعاء لإرسال محتويات النموذج submit callback. كما استخدمنا الخاصية <a href="https://jaredpalmer.com/formik/docs/api/formik#initialvalues-values" rel="external nofollow">initialValues</a> لتزويد السياق بالقيم الأولية على شكل كائنات مفاتيحها أسماء الحقول وقيمها هي القيم الأولية المقابلة. كما تزودنا الصفة <a href="https://jaredpalmer.com/formik/docs/api/formik#onsubmit-values-values-formikbag-formikbag--void--promiseany" rel="external nofollow">onSubmit</a> باستدعاء إرسال محتويات النموذج، حيث ينفَّذ هذا الاستدعاء عندما تُستدعى الدالة <code>handleSubmit</code> شريطة أن لا تظهر أية أخطاء تقييم لمحتويات الحقول. تُستدعى الدالة التي تمثل ابنا مباشرًا للمكوّن <code>Formik</code> من خلال الخصائص <a href="https://jaredpalmer.com/formik/docs/api/formik#formik-render-methods-and-props" rel="external nofollow">props</a> التي تحتوي معلومات متعلقة بالحالة، كما تحتوي أفعالًا كالدالة <code>handleSubmit</code>.
</p>

<p>
	يحتوي المكوّن <code>BodyMassIndexForm</code> ارتباطات الحالة بين السياق وعناصر الإدخال النصيّة. ويُستخدم الخطاف <a href="https://jaredpalmer.com/formik/docs/api/useField" rel="external nofollow">useField</a> للحصول على قيمة حقل أو تعديل قيمته. يقبل <code>useField</code> وسيطًا واحدًا هو اسم الحقل ويعيد مصفوفة من ثلاث كائنات هي [الحقل field, البيانات الوصفية meta، المُساعدات helpers]. يحتوي الكائن <a href="https://jaredpalmer.com/formik/docs/api/useField#fieldinputpropsvalue" rel="external nofollow">field</a> على قيمة الحقل، بينما يحتوي الكائن <a href="https://jaredpalmer.com/formik/docs/api/useField#fieldmetapropsvalue" rel="external nofollow">meta</a> على المعلومات الوصفية للحقل كرسائل الخطأ التي قد يرميها. أما الكائن الأخير <a href="https://jaredpalmer.com/formik/docs/api/useField#fieldhelperprops" rel="external nofollow">helpers</a>، فيحتوي على الأفعال التي تُستخدم لتغيير حالة الحقل كالدالة <code>setValue</code>. وتجدر الإشارة إلى أنّ المكوّن الذي يستخدم الخطاف لابدّ أن يكون ضمن سياق Formik. أي يجب أن يكون هذا المكوّن من أبناء المكوّن <code>Formik</code>.
</p>

<p>
	يمكنك الاطلاع على نسخة تفاعلية عن المثال السابق باسم <a href="https://snack.expo.io/@kalleilv/formik-example" rel="external nofollow">Formik example</a> على موقع Expo Snack.
</p>

<p>
	لقد سبب استخدام الخطاف <code>useField</code> مع المكوّن <code>TextInput</code> شيفرة مكررة. لنعزل هذه الشيفرة ونضعها في مكوّن جديد باسم <code>FormikTextInput</code>، ولننشئ مكوّنًا مخصصًا باسم <code>TextInput</code> لإضافة مظهر مرئي أفضل لعنصر الإدخال النصي. لنثبّت أولًا Formik:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_59" style="">
<span class="pln">npm install formik</span></pre>

<p>
	سننشئ الآن الملف "TextInput.jsx" في المجلد "components" ونضع فيه الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_62" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">TextInput</span><span class="pln"> as </span><span class="typ">NativeTextInput</span><span class="pun">,</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> styles </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">.</span><span class="pln">create</span><span class="pun">({});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">TextInput</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> style</span><span class="pun">,</span><span class="pln"> error</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln">props </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> textInputStyle </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">style</span><span class="pun">];</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">NativeTextInput</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">textInputStyle</span><span class="pun">}</span><span class="pln"> 
    </span><span class="pun">{...</span><span class="pln">props</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">TextInput</span><span class="pun">;</span></pre>

<p>
	لننتقل إلى المكوّن <code>FormikTextInput</code> الذي ينشئ رابطًا لحالة Formik بالمكوّن <code>TextInput</code>. سننشئ الآن الملف "FormikTextInput.jsx" في المجلد "components" ونضع فيه الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_64" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useField </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'formik'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">TextInput</span><span class="pln"> from </span><span class="str">'./TextInput'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Text</span><span class="pln"> from </span><span class="str">'./Text'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> styles </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">.</span><span class="pln">create</span><span class="pun">({</span><span class="pln">
  errorText</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    marginTop</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">FormikTextInput</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln">props </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">field</span><span class="pun">,</span><span class="pln"> meta</span><span class="pun">,</span><span class="pln"> helpers</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useField</span><span class="pun">(</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> showError </span><span class="pun">=</span><span class="pln"> meta</span><span class="pun">.</span><span class="pln">touched </span><span class="pun">&amp;&amp;</span><span class="pln"> meta</span><span class="pun">.</span><span class="pln">error</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">TextInput</span><span class="pln">
        onChangeText</span><span class="pun">={</span><span class="pln">value </span><span class="pun">=&gt;</span><span class="pln"> helpers</span><span class="pun">.</span><span class="pln">setValue</span><span class="pun">(</span><span class="pln">value</span><span class="pun">)}</span><span class="pln">
        onBlur</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> helpers</span><span class="pun">.</span><span class="pln">setTouched</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">)}</span><span class="pln">
        value</span><span class="pun">={</span><span class="pln">field</span><span class="pun">.</span><span class="pln">value</span><span class="pun">}</span><span class="pln">
        error</span><span class="pun">={</span><span class="pln">showError</span><span class="pun">}</span><span class="pln">
        </span><span class="pun">{...</span><span class="pln">props</span><span class="pun">}</span><span class="pln">
      </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">showError </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">errorText</span><span class="pun">}&gt;{</span><span class="pln">meta</span><span class="pun">.</span><span class="pln">error</span><span class="pun">}&lt;/</span><span class="typ">Text</span><span class="pun">&gt;}</span><span class="pln">
    </span><span class="pun">&lt;/&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">FormikTextInput</span><span class="pun">;</span></pre>

<p>
	يمكننا بعد استخدام المكوّن <code>FormikTextInput</code> أن نعيد كتابة المكوّن <code>BodyMassIndexForm</code> في المثال السابق كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_66" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">BodyMassIndexForm</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> onSubmit </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">FormikTextInput</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"mass"</span><span class="pln"> placeholder</span><span class="pun">=</span><span class="str">"Weight (kg)"</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">      </span><span class="pun">&lt;</span><span class="typ">FormikTextInput</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"height"</span><span class="pln"> placeholder</span><span class="pun">=</span><span class="str">"Height (m)"</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">      </span><span class="pun">&lt;</span><span class="typ">TouchableWithoutFeedback</span><span class="pln"> onPress</span><span class="pun">={</span><span class="pln">onSubmit</span><span class="pun">}&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="typ">Calculate</span><span class="pun">&lt;/</span><span class="typ">Text</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="typ">TouchableWithoutFeedback</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">View</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	وكما نرى سيقلص إنشاء المكوّن <code>FormikTextInput</code> الذي يعالج ارتباطات حالة Formik بالمكوّن <code>TextInput</code> الشيفرة اللازمة. ومن الجيد أن تكرر نفس العملية إن استخدَمَت نماذج Formik التي لديك مكونات إدخال بيانات.
</p>

<h2>
	التمرين 10.8
</h2>

<h3>
	10.8 نموذج تسجيل الدخول
</h3>

<p>
	أنجز نموذجًا لتسجيل الدخوّل يرتبط بالمكوّن <code>SignIn</code> الذي أضفناه سابقًا في الملف"SignIn.jsx". يجب أن يحتوي النموذج مربعي إدخال نصيين، أحدهما لاسم المستخدم والآخر لكلمة السر. كما يجب أن يحتوي زرًا لتسليم بيانات النموذج. لا حاجة لكتابة دالة الاستدعاء <code>onSubmit</code>، بل يكفي إظهار قيم الحقول باستخدام الأمر <code>console.log</code> عند تسليم البيانات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_68" style="">
<span class="kwd">const</span><span class="pln"> onSubmit </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">values</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">values</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	تذكّر أن تستخدم المكوّن <code>FormikTextInput</code> الذي أنشأناه سابقّا. كما يمكنك استخدام الخاصية <a href="https://reactnative.dev/docs/textinput#securetextentry" rel="external nofollow">secureTextEntry</a> في المكوّن <code>TextInput</code> لحجب كلمة السر عند كتابتها.
</p>

<p>
	سيبدو نموذج تسجيل الدخول مشابهًا للتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="71745" href="https://academy.hsoub.com/uploads/monthly_2021_07/signin_form_04.png.83e3781353201e7a6fa0e6eeb929a7e4.png" rel=""><img alt="signin_form_04.png" class="ipsImage ipsImage_thumbnailed" data-fileid="71745" data-unique="zz64yfmmr" src="https://academy.hsoub.com/uploads/monthly_2021_07/signin_form_04.thumb.png.685fed67893e70757bae5c2513cdc429.png"></a>
</p>

<h2>
	تقييم النموذج
</h2>

<p>
	تقدم Formik مقاربتين لتقييم النماذج: دالة تقييم أو تخطيط تقييم. فدالة التقييم: هي دالة تُمرر للمكون <code>Formik</code> كقيمة للخاصية <a href="https://jaredpalmer.com/formik/docs/guides/validation#validate" rel="external nofollow">validate</a>. حيث تستقبل الدالة قيم حقول النموذج كوسطاء وتعيد كائنًا يحتوى الأخطاء المحتملة الخاصة بكل حقل. أما تخطيط التقييم فيمرر إلى المكوّن Formik كقيمة للخاصية <a href="https://jaredpalmer.com/formik/docs/guides/validation#validationschema" rel="external nofollow">validationSchema</a>. ويمكن إنشاء هذا التخطيط باستخدام مكتبة تقييم تُدعى <a href="https://github.com/jquense/yup" rel="external nofollow">Yup</a>. لنثبّت هذه المكتبة إذًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_70" style="">
<span class="pln">npm install yup</span></pre>

<p>
	كمثال على ما أوردنا، سننشئ تاليًا تخطيط تقييم لنموذج "مؤشر كتلة الجسم" الذي أنجزناه سابقًا. نريد أن نتحقق أنّ قيمتي كتلة الجسم والطول موجودتان، وأنهما قيم عددية. كذلك نريد أن نتحقق أنّ كتلة الجسم أكبر أو تساوي1، وأنّ الطول أكبر أو يساوي 0.5. تمثل الشيفرة التالية طريقة تنفيذ التخطيط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_72" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> as yup from </span><span class="str">'yup'</span><span class="pun">;</span><span class="pln">
</span><span class="com">// ...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> validationSchema </span><span class="pun">=</span><span class="pln"> yup</span><span class="pun">.</span><span class="pln">object</span><span class="pun">().</span><span class="pln">shape</span><span class="pun">({</span><span class="pln">  
    mass</span><span class="pun">:</span><span class="pln"> yup    
    </span><span class="pun">.</span><span class="pln">number</span><span class="pun">()</span><span class="pln">    
    </span><span class="pun">.</span><span class="pln">min</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Weight must be greater or equal to 1'</span><span class="pun">)</span><span class="pln">    
    </span><span class="pun">.</span><span class="pln">required</span><span class="pun">(</span><span class="str">'Weight is required'</span><span class="pun">),</span><span class="pln">  
    height</span><span class="pun">:</span><span class="pln"> yup    
    </span><span class="pun">.</span><span class="pln">number</span><span class="pun">()</span><span class="pln">    
    </span><span class="pun">.</span><span class="pln">min</span><span class="pun">(</span><span class="lit">0.5</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Height must be greater or equal to 0.5'</span><span class="pun">)</span><span class="pln">      </span><span class="pun">.</span><span class="pln">required</span><span class="pun">(</span><span class="str">'Height is required'</span><span class="pun">),});</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">BodyMassIndexCalculator</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">Formik</span><span class="pln">
      initialValues</span><span class="pun">={</span><span class="pln">initialValues</span><span class="pun">}</span><span class="pln">
      onSubmit</span><span class="pun">={</span><span class="pln">onSubmit</span><span class="pun">}</span><span class="pln">
      validationSchema</span><span class="pun">={</span><span class="pln">validationSchema</span><span class="pun">}</span><span class="pln">    </span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">{({</span><span class="pln"> handleSubmit </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">BodyMassIndexForm</span><span class="pln"> onSubmit</span><span class="pun">={</span><span class="pln">handleSubmit</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;}</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="typ">Formik</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	تجري عملية التحقق افتراضيًا عند حدوث أي تغيير في قيم حقول النموذج، وكذلك عند استدعاء الدالة <code>handleSubmit</code>. فإن أخفق التحقق، لن تُستدعى الدالة التي تمرر إلى الكائن <code>Formik</code> عبر الخاصية <code>onSubmit</code>
</p>

<p>
	يعرض المكوًن <code>FormikTextInput</code> الذي أنشأناه سابقًا رسائل الخطأ المتعلقة بالحقل إن وقع الخطأ الموافق لها ولُمس الحقل. أي في الحالة التي يتلقى فيها الحقل تركيز الدخل ثم يفقده:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_74" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">FormikTextInput</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln">props </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">field</span><span class="pun">,</span><span class="pln"> meta</span><span class="pun">,</span><span class="pln"> helpers</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useField</span><span class="pun">(</span><span class="pln">name</span><span class="pun">);</span><span class="pln">

  </span><span class="com">// Check if the field is touched and the error message is present</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> showError </span><span class="pun">=</span><span class="pln"> meta</span><span class="pun">.</span><span class="pln">touched </span><span class="pun">&amp;&amp;</span><span class="pln"> meta</span><span class="pun">.</span><span class="pln">error</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">TextInput</span><span class="pln">
        onChangeText</span><span class="pun">={(</span><span class="pln">value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> helpers</span><span class="pun">.</span><span class="pln">setValue</span><span class="pun">(</span><span class="pln">value</span><span class="pun">)}</span><span class="pln">
        onBlur</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> helpers</span><span class="pun">.</span><span class="pln">setTouched</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">)}</span><span class="pln">
        value</span><span class="pun">={</span><span class="pln">field</span><span class="pun">.</span><span class="pln">value</span><span class="pun">}</span><span class="pln">
        error</span><span class="pun">={</span><span class="pln">showError</span><span class="pun">}</span><span class="pln">
        </span><span class="pun">{...</span><span class="pln">props</span><span class="pun">}</span><span class="pln">
      </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">{</span><span class="com">/* Show the error message if the value of showError variable is true  */</span><span class="pun">}</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">showError </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">errorText</span><span class="pun">}&gt;{</span><span class="pln">meta</span><span class="pun">.</span><span class="pln">error</span><span class="pun">}&lt;/</span><span class="typ">Text</span><span class="pun">&gt;}</span><span class="pln">
    </span><span class="pun">&lt;/&gt;</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<h2>
	التمرين 10.9
</h2>

<h3>
	10.9 تقييم نموذج تسجيل الدخول
</h3>

<p>
	تحقق أنّ حقلي كلمة السر واسم المستخدم في نموذج تسجيل الدخول إجباريين. وتذكر أن لا تستدعي الدالة <code>onSubmit</code> التي أنجزناها في التمرين السابق إن أخفق تقييم النموذج.
</p>

<p>
	سيعرض المكوّن <code>FormikTextInput</code> حاليًا رسالة خطأ إن حدث خطأ في الحقل الذي تم لمسه، عزز مظهر هذه الرسالة بجعل نصها أحمر اللون.
</p>

<p>
	بالإضافة إلى رسالة الخطأ، إجعل إطار حقل الإدخال أحمر اللون ليشير ذلك إلى وقوع خطأ ضمنه. وتذكر أن المكوّن <code>FormikTextInput</code> سيعطي القيمة "true" للخاصية <code>error</code> العائدة للمكوّن <code>TextInput</code> عند حدوث أية أخطاء ضمن الحقل. يمكنك استعمال قيمة هذه الخاصية لإضافة تنسيق شرطي للمكوّن <code>TextInput</code>.
</p>

<p>
	سيبدو شكل نموذج تسجيل الدخول عند حدوث خطأ في أحد الحقول قريبًا من الشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="71746" href="https://academy.hsoub.com/uploads/monthly_2021_07/signin_form_field_error_05.png.86922c9857e7aa3fec79f14d57c0a832.png" rel=""><img alt="signin_form_field_error_05.png" class="ipsImage ipsImage_thumbnailed" data-fileid="71746" data-unique="8n5g1mfmv" src="https://academy.hsoub.com/uploads/monthly_2021_07/signin_form_field_error_05.thumb.png.abbd1d32eda3944eeb282cca41626121.png"></a>
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>ملاحظة</strong>: إن الرقم المرجعي للون الأحمر المستخدم هنا هو d73a4a#.
	</p>
</blockquote>

<h2>
	الشيفرة الخاصة بمنصة
</h2>

<p>
	من أعظم ميزات استخدام React Native هي أننا لن نهتم بالمنصة التي سيعمل عليها التطبيق سواء Android أو iOS. لكن، قد تصادفنا حالات نضطر فيها إلى تنفيذ شيفرة خاصة بمنصة محددة. من هذه الحالات مثلًا، كتابة مكوّنات بطرق مختلفة لتلائم منصات مختلفة.
</p>

<p>
	يمكن الوصول إلى المنصة التي يستخدمها المستخدم من خلال الثابت <code>Platform.OS</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_76" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Platform</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Text</span><span class="pun">,</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react-native'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> styles </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">.</span><span class="pln">create</span><span class="pun">({</span><span class="pln">
  text</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    color</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Platform</span><span class="pun">.</span><span class="pln">OS </span><span class="pun">===</span><span class="pln"> </span><span class="str">'android'</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> </span><span class="str">'green'</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">'blue'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">WhatIsMyPlatform</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">Text</span><span class="pln"> style</span><span class="pun">={</span><span class="pln">styles</span><span class="pun">.</span><span class="pln">text</span><span class="pun">}&gt;</span><span class="typ">Your</span><span class="pln"> platform is</span><span class="pun">:</span><span class="pln"> 
    </span><span class="pun">{</span><span class="typ">Platform</span><span class="pun">.</span><span class="pln">OS</span><span class="pun">}&lt;/</span><span class="typ">Text</span><span class="pun">&gt;;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	يأخذ هذا الثابت إحدى القيمتين: android أو ios. يمكن كتابة شيفرة مخصصة لمنصة أيضًا مستفيدين من التابع <code>Platform.select</code>. إذ يمرر للتابع كائن قد تأخذ مفاتيحه إحدى القيم التالية: ios أو android أو native أو default، ويعيد القيمة الأكثر ملائمة للمنصة التي يعمل عليها المستخدم. يمكننا إعادة كتابة المتغير <code>styles</code> في المثال السابق باستعمال التابع <code>Platform.select</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_78" style="">
<span class="kwd">const</span><span class="pln"> styles </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StyleSheet</span><span class="pun">.</span><span class="pln">create</span><span class="pun">({</span><span class="pln">
  text</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    color</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Platform</span><span class="pun">.</span><span class="pln">select</span><span class="pun">({</span><span class="pln">
      android</span><span class="pun">:</span><span class="pln"> </span><span class="str">'green'</span><span class="pun">,</span><span class="pln">
      ios</span><span class="pun">:</span><span class="pln"> </span><span class="str">'blue'</span><span class="pun">,</span><span class="pln">
      </span><span class="kwd">default</span><span class="pun">:</span><span class="pln"> </span><span class="str">'black'</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">}),</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	وبالإمكان أيضًا استخدام التابع <code>Platform.select</code> لطلب مكوّن خاص بمنصة محددة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_80" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">MyComponent</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Platform</span><span class="pun">.</span><span class="pln">select</span><span class="pun">({</span><span class="pln">
  ios</span><span class="pun">:</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'./MyIOSComponent'</span><span class="pun">),</span><span class="pln">
  android</span><span class="pun">:</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'./MyAndroidComponent'</span><span class="pun">),</span><span class="pln">
</span><span class="pun">})();</span><span class="pln">

</span><span class="pun">&lt;</span><span class="typ">MyComponent</span><span class="pln"> </span><span class="pun">/&gt;;</span></pre>

<p>
	إنّ الطريقة الأكثر تقدّمًا لكتابة وإدراج مكوّنات خاصة بمنصة محددة أو أية أجزاء من الشيفرة، هي استخدام ملفات لها إحدى اللاحقتين "ios.jsx." أو "android.jsx.". وانتبه إلى إمكانية استخدام أية لاحقة يمكن للمجمع أن يميزها مثل "js.". إذ يمكن مثلًا أن ننشئ ملفات باسم "Button.ios.jsx" أو باسم "Button.android.jsx" بحيث نستطيع إدراجها في شيفرتنا كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2870_82" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Button</span><span class="pln"> from </span><span class="str">'./Button'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">PlatformSpecificButton</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="typ">Button</span><span class="pln"> </span><span class="pun">/&gt;;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	وهكذا ستضم حزمة Android للتطبيق المكوّن المعرّف في الملف "Button.android.jsx"، بينما تضم حزمة iOS المكوّن المعرّف في الملف "Button.ios.jsx".
</p>

<h2>
	التمرين 10.10
</h2>

<h3>
	10.10 خط خاص بمنصة محددة
</h3>

<p>
	حُددت عائلة الخط الذي نستخدمه حاليًا في تطبيقنا بأنها "System" ضمن إعدادات تهيئة السمة الموجودة في الملف "theme.js". استخدم بدلًا من الخط "system" خطًا من خطوط العائلة <a href="https://en.wikipedia.org/wiki/Sans-serif" rel="external nofollow">Sans-serif</a> ليكون خطًا خاصًا بكل منصة. استخدم الخط "Roboto" في منصة Android والخط Arial في منصة iOS. يمكن أن تُبقي "System" مثل خط افتراضي.
</p>

<p>
	هذا هو التمرين الأخير في هذا الفصل، وقد حان الوقت لتسليم التمارين إلى GitHub والإشارة إلى أنك أكملتها في <a href="https://studies.cs.helsinki.fi/stats/courses/fs-react-native-2020" rel="external nofollow">منظومة تسليم التمارين</a>. انتبه إلى وضع الحلول في القسم 2 ضمن المنظومة.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://fullstackopen.com/en/part10/react_native_basics" rel="external nofollow">React Native basics</a> من سلسلة <a href="https://fullstackopen.com/en/" rel="external nofollow">Deep Dive Into Modern Web Development</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-react-native-r1264/" rel="">مدخل إلى React Native</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D8%AD%D8%B1%D9%8A%D9%83-%D9%81%D9%8A-react-native-r925/" rel="">مدخل إلى التحريك في React Native</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1265</guid><pubDate>Sun, 11 Jul 2021 15:00:00 +0000</pubDate></item><item><title>&#x645;&#x62F;&#x62E;&#x644; &#x625;&#x644;&#x649; React Native</title><link>https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-react-native-r1264/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_07/60eb27bbd8bbe_Introduction-to-React-NativeA0.png.98447bad4641c10a67c47ef515869ef0.png" /></p>

<p>
	تتطلب كتابة تطبيقات أصليّة Native لنظامي التشغيل iOS وAndroid تقليديًا استخدام لغات برمجة وبيئات تطوير خاصة بكل منصة. فقد استخدمت لغات مثل Objective C وSwift لتطوير تطبيقات iOS، ولغات مبنية على JVM مثل Java وScala وKotlin لتطوير تطبيقات موجّهة لنظام Android. وبالتالي وجب من الناحية التقنية كتابة تطبيقين منفصلين وبلغتي برمجة مختلفتين من أجل العمل على المنصتين السابقتين، وهذا ما تطلب الكثير من الموارد.
</p>

<p>
	لقد كان استخدام المتصفح كمحرك لتصيير التطبيقات إحدى المقاربات الأكثر شعبية في توحيد تطوير التطبيقات للمنصات المختلفة. وكانت المنصة <a href="https://wiki.hsoub.com/Cordova/" rel="external">Cordova</a> من أشهر المنصات التي استخدمت في تطوير تطبيقات تعمل على منصات مختلفة. إذ تسمح هذه المنصة بتطوير تطبيقات قادرة على العمل على منصات مختلفة اعتمادًا على تقنيات الويب المعيارية مثل HTML5 وCSS3 وJavaScript. تعمل تطبيقات Cordova ضمن المتصفح المدمج في جهاز المستخدم، وبالتالي لن تحقق الأداء ولا المظهر الذي يمنحه استخدام التطبيقات الأصلية التي تستخدم مكوّنات واجهة المستخدم الأصلية.
</p>

<p>
	تقدم <a href="https://wiki.hsoub.com/ReactNative/" rel="external">React Native</a> إطار عمل لتطوير تطبيقات أصلية لمنصتي iOS وAndroid باستخدام JavaScript وReact. حيث تؤمن مجموعة من المكوّنات التي تعمل على منصات مختلفة وتستخدم خلف الستار المكوّنات الأصلية بالمنصة التي يعمل عليها التطبيق. وباستخدام React Native يمكننا استحضار كل الميزات المألوفة التي تقدمها React مثل JSX والمكوّنات components والخصائص props والحالة state والخطافات hooks، لتطوير تطبيقات أصلية لمنصة. وفوق كل ذلك، سنتمكن من استخدام مكتبات مألوفة بالنسبة لنا ضمن بيئة عمل React مثل <a href="https://react-redux.js.org/" rel="external nofollow">react-redux</a> و<a href="https://github.com/apollographql/react-apollo" rel="external nofollow">react-apollo</a> و<a href="https://reacttraining.com/react-router/core/guides/quick-start" rel="external nofollow">react-router</a> وغيرها الكثير.
</p>

<p>
	تعتبر سرعة التطوير ومنحى التعلم المتصاعد للمطورين الذين ألفوا React، من أبرز الفوائد التي تمنحها React Native. وإليكم اقتباسًا تشجيعيًا ورد في مقال لمدونة Coinbase بعنوان <a href="https://blog.coinbase.com/onboarding-thousands-of-users-with-react-native-361219066df4" rel="external nofollow">Onboarding thousands of users with React Native</a>، أو بالعربية "ضم آلاف المستخدمين إلى React Native"، حول فوائد React Native.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		لو أردنا اختصار فوائد استخدام React Native في كلمة واحدة، لكانت "السرعة". فلقد تمكن فريقنا من ضم مهندسين في وقت أقل، كما شاركوا كميات أكبر من الشيفرات (والتي نتوقع أن تعزز الإنتاجية المستقبلية)، وكانت سرعة تسليم الميزات أكبر -بكل تأكيد- مقارنة بمقاربة استخدام الشيفرة الأصلية الصرفة.
	</p>
</blockquote>

<h2>
	حول هذا القسم
</h2>

<p>
	سنتعلم خلال هذا القسم بناء تطبيقات React Native فعلية من البداية إلى النهاية. وسنتعلم مفاهيم مثل مكونات React Native البنيوية core components، وكيفية بناء واجهات مستخدم جميلة، وكيف سنتصل مع الخادم، كما سنتعلم كيفية اختبار تطبيقات React Native.
</p>

<p>
	سنعمل على تطوير تطبيق لتقييم مستودعات <a href="https://github.com/" rel="external nofollow">GitHub</a>. وسيمتلك التطبيق ميزاتٍ كفرز وانتقاء المستودعات التي نقيّمها، وكذلك ميزة تسجيل مستخدم جديد، وتسجيل دخول مستخدم، وإنشاء مذكرة تقييم جديدة. سنستخدم واجهة خلفية جاهزة لكي ينصب تركيزنا على تطوير التطبيق باستخدام React Native فقط.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="71691" href="https://academy.hsoub.com/uploads/monthly_2021_07/react_native_app_01.png.476899d6f5ce1e5a651d62cb8ced0707.png" rel=""><img alt="react_native_app_01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="71691" data-unique="lg2b84klp" src="https://academy.hsoub.com/uploads/monthly_2021_07/react_native_app_01.thumb.png.36bdfe27098906ee8669ddd59e8d8f97.png"></a>
</p>

<p>
	ينبغي أن تسلم كل تمارين هذا القسم إلى مستودع GitHub واحد، سيضم مع الوقت الشيفرة الكلية للتطبيق. ستجد أيضًا حلًا نموذجيًا لكل مقال من هذا الجزء يمكنك استخدامه لإكمال التمارين الناقصة عند تسليمها. تعتمد هيكلية القسم على فكرة تطوير التطبيق تدريجيًا مع تقدمك في قراءة مادة القسم العلمية. فلا تنتظر حتى تصل إلى التمارين لتبدأ بتطوير تطبيقك، بل طوّره بما يتناسب مع المرحلة التي وصلت إليها في قراءة مادة القسم.
</p>

<p>
	سيعتمد هذا القسم بشدة على مفاهيم ناقشناها في أقسام سابقة. لذا وقبل أن تبدأ رحلتك هنا، عليك امتلاك بعض المعارف الأساسية في JavaScript وReact وGraphQL. لن تحتاج إلى معرفة عميقة في تطوير التطبيقات من ناحية الخادم، فكل الشيفرة التي تحتاجها للواجهة الخلفية محضرة مسبقًا. مع ذلك، سننفذ طلبات اتصال شبكية من خلال تطبيقات React Native مثل استعلامات GraphQL. ننصحك بإكمال الأقسام Part 1 وPart 2 وPart 5 وPart 7 وPart 8 قبل البدء بهذا القسم.
</p>

<h2>
	تسليم التمرينات والحصول على الساعات المعتمدة
</h2>

<p>
	ستُسلّم التمارين إلى <a href="https://studies.cs.helsinki.fi/stats/courses/fs-react-native-2020" rel="external nofollow">منظومة استلام التمارين</a> كما هو الحال في الأقسام السابقة. لكن انتبه إلى أنّ تمارين هذا المقال ستُسلّم إلى نسخةً أخرى من المنهاج تختلف عن النسخة التي سلمنا فيها تمارين الأقسام السابقة. فالأقسام من 1 إلى 4 في منظومة التسليم في هذه النسخة تشير إلى الفصول من a إلى d من هذا القسم. ويعني هذا أن عليك تسليم تمارين كل مقال على حدى ابتداءً من هذا المقال "مدخل إلى React Native" والذي سيحمل الاسم "Part 1" في منظومة تسليم التمارين.
</p>

<p>
	ستحصل في هذا القسم على ساعات معتمدة بناء على عدد التمارين التي تُكملها. فستحصل على ساعة معتمدة عند إكمالك 19 تمرينًا على الأقل، وعلى ساعتين معتمدتين عند إكمالك 26 تمرينُا على الأقل.
</p>

<p>
	وإن أردت الحصول على الساعات عند إكمالك للتمارين، أعلمنا من خلال منظومة التسليم بأنك أكملت المنهاج.
</p>

<p style="text-align: center;">
	<img alt="submission_system_course_complete_02.png" class="ipsImage ipsImage_thumbnailed" data-fileid="71692" data-unique="ayfof6865" src="https://academy.hsoub.com/uploads/monthly_2021_07/submission_system_course_complete_02.png.49836e145988db4cef5e549fd6b4fad8.png"></p>

<p>
	وتشير الملاحظة "exam done in Moodle" إلى <a href="https://fullstackopen.com/en/part0/general_info#sign-up-for-the-exam" rel="external nofollow">امتحان منهاج التطوير الشامل لتطبيقات الويب</a>، والذي عليك اجتيازه حتى تحصل على ساعاتك المعتمدة المخصصة لهذا القسم.
</p>

<p>
	يمكنك الحصول على شهادة إكمال هذا القسم بالنقر على إحدى أيقونات "العلم". حيث ترتبط أيقونات الأعلام باللغة التي نُظِّمت بها الشهادة. وانتبه إلى ضرورة إكمال ما يعادل ساعة معتمدة من تمارين هذا القسم لتحصل على الشهادة.
</p>

<h2>
	تهيئة التطبيق
</h2>

<p>
	علينا تهيئة بيئة التطوير حتى نبدأ بالعمل مع تطبيقنا. وكنا قد تعاملنا في الأقسام السابقة مع أدوات مفيدة في ضبط وتهيئة تطبيقات React بسرعة مثل برنامج "create react app". تمتلك React Native لحسن الحظ أدواتٍ مشابهة أيضًا.
</p>

<p>
	سنستخدم لتطوير تطبيقنا منصة <a href="https://docs.expo.io/versions/latest/" rel="external nofollow">Expo</a>، وهي منصة تبسط إعداد وتطوير وبناء ونشر تطبيقات React Native. لنبدأ إذًا بتثبيت واجهة سطر الأوامر الخاصة بهذه المنصة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2258_9" style="">
<span class="pln">npm install </span><span class="pun">--</span><span class="pln">global expo</span><span class="pun">-</span><span class="pln">cli</span></pre>

<p>
	يمكننا الآن تهيئة مشروعنا داخل المجلد "rate-repository-app" بتنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2258_11" style="">
<span class="pln">expo init rate</span><span class="pun">-</span><span class="pln">repository</span><span class="pun">-</span><span class="pln">app </span><span class="pun">--</span><span class="pln">template expo</span><span class="pun">-</span><span class="pln">template</span><span class="pun">-</span><span class="pln">blank@sdk</span><span class="pun">-</span><span class="lit">38</span></pre>

<p>
	يضبط الوسيط "sdk-38@" نسخة "Expo SDK" على 38 وهي النسخة التي تدعم النسخة 0.62 من React Native. وقد يتسبب لك استخدام نسخة مختلفة من "Expo SDK" مشاكل عند تتبع المادة التعليمية في هذا القسم. وتجدر الإشارة إلى <a href="https://docs.expo.io/introduction/why-not-expo/" rel="external nofollow">محدودية Expo في بعض النقاط</a> موازنةً بواجهة اللغة المشتركة CLI التي تستخدمها React Native، لكن لن تؤثر هذه المحدودية على التطبيق الذي سننفذه في هذا القسم.
</p>

<p>
	افتح المجلد "rate-repository-app" الذي أنشأناه عند تهيئة التطبيق باستخدام محرر شيفرة مثل <a href="https://code.visualstudio.com/" rel="external nofollow">Visual Studio Code</a>.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="71688" href="https://academy.hsoub.com/uploads/monthly_2021_07/app_directory_03.png.1c4ba6fea1ec2a1171378c3ddb28498b.png" rel=""><img alt="app_directory_03.png" class="ipsImage ipsImage_thumbnailed" data-fileid="71688" data-unique="52a7v67dh" src="https://academy.hsoub.com/uploads/monthly_2021_07/app_directory_03.thumb.png.a17524e2a8aa215538fe934caa22365f.png"></a>
</p>

<p>
	قد ترى بعض الملفات والمجلدات المألوفة ضمنه مثل "package.json" و"node_modules"، كما سترى أيضًا الملفات الأكثر ارتباطًا بالتطبيق مثل "app.json" الذي يحتوي على أوامر التهيئة الخاصة بالمنصة Expo، والملف "App.js" الذي يمثل المكوِّن الجذري لتطبيقنا. لا تغير اسم الملف "App.js" ولا تنقله إلى مجلد آخر، لأن Expo سيدرجه <a href="https://docs.expo.io/versions/latest/sdk/register-root-component/" rel="external nofollow">لتسجيل المكوّن الجذري</a>.
</p>

<p>
	لنلق نظرة على قسم السكربتات في الملف "package.json":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2258_13" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"start"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"expo start"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"android"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"expo start --android"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"ios"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"expo start --ios"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"web"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"expo start --web"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"eject"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"expo eject"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيشغّل تنفيذ الأمر <code>npm start</code> المجمّع <a href="https://facebook.github.io/metro/" rel="external nofollow">Metro</a> وهو مُجمّع JavaScript أي JavaScript bundler يستخدم مع React Native، ويمكن أن نعده مشابهًا لبرنامج <a href="https://webpack.js.org/" rel="external nofollow">Webpack</a> لكنه خاص ببيئة React Native. كما ينبغي أن تُشغَّل أدوات تطوير Expo على العنوان <a href="http://localhost:19002/" rel="external nofollow">http://localhost:19002</a> من خلال المتصفح. وهذه الأدوات مفيدةً جدًا في عرض سجلات تنفيذ التطبيق، بالإضافة إلى تشغيل التطبيق من خلال المقلّد emulator، أو من خلال تطبيق جوّال Expo. سنعود إلى هذين الأخيرين لاحقًا، إذا علينا أولًا تشغيل التطبيق في المتصفح بالنقر على الزر "Run":
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="71689" href="https://academy.hsoub.com/uploads/monthly_2021_07/app_start_in_browser_04..png.fed234af0fd8cd671419fe2f18b8e3d4.png" rel=""><img alt="app_start_in_browser_04..png" class="ipsImage ipsImage_thumbnailed" data-fileid="71689" data-unique="ftl1ph5j3" src="https://academy.hsoub.com/uploads/monthly_2021_07/app_start_in_browser_04..thumb.png.2b2c89cc4fe4ca42d4b06204907bbcc2.png"></a>
</p>

<p>
	ستجد بعد النقر على الرابط أن النص المكتوب في الملف "App.js" سيظهر على نافذة المتصفح. افتح هذا الملف باستخدام أي محرر مناسب واجر تغييرًا بسيطًا على النص الموجود في المكوّن <code>Text</code>. يجب أن تظهر التعديلات التي أجريتها مباشرة على شاشة المتصفح بعد أن تحفظ التغيرات.
</p>

<h2>
	إعداد بيئة التطوير
</h2>

<p>
	لقد ألقينا نظرة أولى على تطبيقنا من خلال متصفح Expo. وعلى الرغم من فائدة هذا المتصفح، إلا أنه يقدم محاكاة بدائية للبيئة الأصلية. سنتعرف تاليًا على بعض البدائل المتوفرة لدينا بما يتعلق ببيئة التطوير.
</p>

<p>
	يمكن تقليد أجهزة iOS وAndroid كالأجهزة اللوحية والهواتف عن طريق الحاسوب وباستخدام مقلّدات Emulators خاصة. وهذا أمر مفيد جدًا عند تطوير التطبيقات باستخدام React Native. يمكن لمستخدمي نظام التشغيل macOS استخدام مقلدات iOS وAndroid على حواسيبهم، أما مستخدمي أنظمة التشغيل الأخرى مثل Linux وWindows، فليس بوسعهم سوى العمل مع مقلدات Android. اتبع التعليمات التالية حول إعداد المقلّد، بناء على نظام التشغيل الذي يستخدمه حاسوبك:
</p>

<ul>
<li>
		<a href="https://docs.expo.io/versions/v37.0.0/workflow/android-studio-emulator/" rel="external nofollow">Set up Android emulator with Android Studio</a> لأي نظام تشغيل
	</li>
	<li>
		<a href="https://docs.expo.io/versions/v37.0.0/workflow/ios-simulator/" rel="external nofollow">Set up iOS simulator with Xcode</a> لنظام التشغيل macOS
	</li>
</ul>
<p>
	بعد تثبيت المقلّد واختباره، شغّل أدوات تطوير Expo كما فعلنا سابقًا عن طريق الأمر <code>npm start</code>. وبناء على المقلد الذي تستخدمه، إنقر الزر "Run" على مقلد أجهزة Android، أو "Run" على رابط محاكي iOS. ينبغي أن يتصل Expo بالمقلد بعد النقر على الرابط، وسترى التطبيق عليه. كن صبورًا، فقد يستغرق الأمر وقتًا.
</p>

<p>
	بالإضافة إلى المقلدات، ستجد أنّ هناك طريقة غاية في الأهمية لتطوير تطبيقات React Native باستخدام Expo، وهي تطبيق جوّال Expo. فباستخدام هذا التطبيق، ستتمكن من استعراض تطبيقك عبر جهازك النقّال الفعلي، وهذا ما يمنحك خبرة أكثر موازنةً بالمقلدات. ثبّت أولًا تطبيق جوّال Expo باتباع التعليمات الموجودة في <a href="https://docs.expo.io/versions/latest/get-started/installation/#2-mobile-app-expo-client-for-ios" rel="external nofollow">توثيق Expo</a>. وانتبه إلى أنّ تطبيق جوّال Expo لن يعمل إن لم يكن جهازك النقًال متصلًا بنفس الشبكة المحلية (نفس الشبكة اللاسلكية على سبيل المثال) التي يتصل بها حاسوب التطوير الذي تعمل عليه.
</p>

<p>
	شغل تطبيق جوّال Expo عندما تنتهي من تثبيته. إن لم تكن قد شغّلت أدوات تطوير Expo بعد، فافعل ذلك مستخدمًا الأمر <code>npm start</code>. سترى في الزاوية اليسارية السفلى من نافذة أدوات التطوير شيفرة QR. امسح هذه الشيفرة باستخدام تطبيق جوّال Expo عن طريق الضغط على الرابط "Scan QR Code". ينبغي أن يبدأ تطبيق Expo ببناء مجمّع JavaScript، ومن المفترض أن ترى تطبيقك وهو يعمل بعد انتهاء عملية التجميع. وهكذا ستتمكن في كل مرة تفتح فيها تطبيقك باستخدام تطبيق جوّال Expo من الوصول إليه دون الحاجة إلى مسح شيفرة QR مجددًا، وذلك بالضغط عليه في قائمة التطبيقات التي شُغّلت مؤخرًا، والتي تظهر في نافذة العرض "Project".
</p>

<h2>
	التمرين 10.1
</h2>

<h3>
	10.1 تهيئة التطبيق
</h3>

<p>
	استخدم واجهة سطر أوامر Expo لتهيئة تطبيقك، وجهّز بيئة التطوير باستخدام المقلد أو تطبيق جوّال Expo. ننصحك بتجريب الأسلوبين السابقين لتحدد البيئة الأنسب بالنسبة لك. لا تهتم كثيرًا لاسم التطبيق، فيمكنك أن تستخدم الاسم "rate-repository-app".
</p>

<p>
	عليك <a href="https://github.com/new" rel="external nofollow">إنشاء مستودع GitHub جديد</a> لتسليم هذا التمرين وغيره من التمارين اللاحقة، ويمكنك تسمية هذا المستودع باسم التطبيق الذي قمت بتهيئته عند تنفيذك الأمر <code>expo init</code>. إن قررت إنشاء مستودع خاص، أضف المستخدم <a href="https://github.com/Kaltsoon" rel="external nofollow">Kaltsoon</a> كمشارك لك <a href="https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/inviting-collaborators-to-a-personal-repository" rel="external nofollow">collaborator</a> في هذا المستودع، وذلك للتحقق من التمارين التي أنجزتها وسلمتها.
</p>

<p>
	نفذ بعد إنشائك المستودع الأمر <code>git init</code> ضمن المجلد الجذري لمشروعك للتأكد من تهيئة المستودع كمستودع Git. ولإضافة المستودع الجديد إلى قائمة العمل عن بعد نفذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2258_15" style="">
<span class="pln">git remote add origin git@github</span><span class="pun">.</span><span class="pln">com</span><span class="pun">:&lt;</span><span class="pln">YOUR\*GITHUB\*USERNAME</span><span class="pun">&gt;/&lt;</span><span class="pln">NAME\*OF\*YOUR_REPOSITORY</span><span class="pun">&gt;.</span><span class="pln">git</span></pre>

<p>
	وتذكر أن تستبدل القيم داخل الأقواس بما يناسب عندك كتابتك لسطر الأوامر. ادفع أخيرًا بالتغييرات التي أجريتها إلى المستودع وسيكون كل شيء على ما يرام.
</p>

<h2>
	المدقق ESLint
</h2>

<p>
	الآن، وقد ألفنا بيئة التطوير سنعمل على تحسين مهاراتنا في التطوير وذلك بتهيئة مدقق للشيفرة. سنستخدم المدقق <a href="https://eslint.org/" rel="external nofollow">ESLint</a> الذي تعاملنا معه سابقًا، وكالعادة سنبدأ بتثبيت الاعتماديات اللازمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2258_17" style="">
<span class="pln">npm install </span><span class="pun">--</span><span class="pln">save</span><span class="pun">-</span><span class="pln">dev eslint babel</span><span class="pun">-</span><span class="pln">eslint eslint</span><span class="pun">-</span><span class="pln">plugin</span><span class="pun">-</span><span class="pln">react</span></pre>

<p>
	سنضع القواعد التالية للمدقق داخل الملف ذو اللاحقة "eslintrc." والموجود في المجلد "rate-repository-app":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2258_19" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"plugins"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"react"</span><span class="pun">],</span><span class="pln">
  </span><span class="str">"settings"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"react"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"detect"</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"extends"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"eslint:recommended"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"plugin:react/recommended"</span><span class="pun">],</span><span class="pln">
  </span><span class="str">"parser"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"babel-eslint"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"env"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"browser"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="str">"rules"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"react/prop-types"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"off"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"semi"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"error"</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ومن ثم سنضيف السكربت "lint" إلى محتويات الملف "package.json"، وذلك للتحقق من تطبيق قواعد المدقق في الملفات المحددة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2258_21" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  </span><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"start"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"expo start"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"android"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"expo start --android"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"ios"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"expo start --ios"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"web"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"expo start --web"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"eject"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"expo eject"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"lint"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"eslint ./src/**/*.{js,jsx} App.js --no-error-on-unmatched-pattern"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحظ أننا استخدمنا الفواصل لإنهاء سطر الشيفرة على نقيض ما فعلنا في الأقسام 1-8، لذلك أضفنا القاعدة <a href="https://eslint.org/docs/rules/semi" rel="external nofollow">semi</a> للتأكد من ذلك.
</p>

<p>
	نستطيع الآن التأكد من خضوع ملفات JavaScript الموجودة في المجلد "src" وكذلك الملف "App.js" لقواعد المدقق، وذلك بتنفيذ الأمر <code>npm run lint</code>. سنضع ملفات الشيفرة لاحقًا في المجلد "src"، لكن طالما أننا لم نضف أية ملفات ضمنه حتى اللحظة، سنضطر إلى تفعيل الراية "no-error-on-unmatched-pattern". حاول إن استطعت أيضًا دمج المدقق ESlint مع محرر الشيفرة الذي تعمل عليه. فإن كنت تعمل على Visual Studio Code، يمكنك فعل ذلك بتفقد قسم الموّسعات extensions والتأكد من تثبيت وتفعيل موسِّع ESlint.
</p>

<p style="text-align: center;">
	<img alt="vsc_eslint_extension_05.png" class="ipsImage ipsImage_thumbnailed" data-fileid="71693" data-unique="27ghl359c" src="https://academy.hsoub.com/uploads/monthly_2021_07/vsc_eslint_extension_05.png.d249f75d6207d7dd8b334473b2d8a27f.png"></p>

<p>
	تمثل قواعد تهيئة المدقق السابقة، القواعد الأساسية للتهيئة. يمكنك أن تعدّل بحرية على هذه القواعد وأن تضيف ملحقات جديدة plugins إن شعرت بالحاجة إلى ذلك.
</p>

<h2>
	التمرين 10.2
</h2>

<h3>
	10.2 إعداد وضبط المدقق ESLint
</h3>

<p>
	هيئ المدقق في مشروعك حتى تتمكن من تدقيق ملفات الشيفرة عند تنفيذك الأمر. ويفضّل عند استخدامك المدقق أن تدمجه مع محررك لكي تستفيد من كامل إمكانياته.
</p>

<p>
	هذا هو التمرين الأخير في هذا المقال، وقد حان الوقت لتسليم التمارين إلى GitHub والإشارة إلى أنك أكملتها في <a href="https://studies.cs.helsinki.fi/stats/courses/fs-react-native-2020" rel="external nofollow">منظومة تسليم التمارين</a>. انتبه إلى وضع الحلول في القسم 1 ضمن المنظومة.
</p>

<h2>
	عرض سجلات العمل
</h2>

<p>
	يمكن الاستفادة من بيئة تطوير Expo في عرض سجلات العمل للتطبيق، كما يمكن متابعة رسائل الخطأ والتحذير ضمن المقلّد أو ضمن واجهة تطبيق الجوّال. ستظهر رسالة الخطأ باللون الأحمر فوق مكان الخطأ، بينما يمكن عرض رسائل التحذير بالضغط على رسالة التحذير في أسفل الشاشة. كما يمكنك استخدام الأمر <code>console.log</code> لطباعة رسائل محددة، وذلك لغايات التنقيح وكشف الأخطاء.
</p>

<p>
	لتجريب ذلك عمليًا، شغّل أدوات تطوير Expo بتنفيذ الأمر <code>npm start</code>، ثم شغّل التطبيق باستخدام المقلّد أو تطبيق الجوّال. ستتمكن عندها من رؤية الأجهزة المتصلة بحاسوبك تحت العنوان "Metro Bundler" في الزاوية العليا اليسارية من نافذة أدوات التطوير:
</p>

<p style="text-align: center;">
	<img alt="connected_devices_to_expo_06.png" class="ipsImage ipsImage_thumbnailed" data-fileid="71690" data-unique="oa3ugzuo9" src="https://academy.hsoub.com/uploads/monthly_2021_07/connected_devices_to_expo_06.png.7580a03380c86f2ad9b5112bdea4307c.png"></p>

<p>
	انقر على الجهاز لفتح سجلات العمل عليه. افتح بعد ذلك الملف "App.js" وأضف رسالةً ما باستخدام الأمر
</p>

<p>
	<code>console.log</code> إلى المكوِّن App. سترى رسالتك ضمن السجلات بمجرد أن تحفظ التغييرات التي أجريتها على الملف.
</p>

<h2>
	استخدام منقح الأخطاء Debugger
</h2>

<p>
	قد يكون فحص الرسائل التي كُتبت باستخدام الأسلوب <code>console.log</code> مفيدًا، لكن سيتطلب الأمر فهمًا أوسع إن كنت تحاول أن تجد ثغرةً ما أو أن تفهم سير عمل التطبيق. فربما سنهتم -على سبيل المثال- بخصائص أو بحالة مكوِّن محدد، أو بالاستجابة لطلبٍ محدد على الشبكة، لذلك استخدمنا أدوات المطوّر التي يؤمنها المتصفح لإنجاز هذا العمل سابقًا. بالمقابل سنجد أنّ المنقّح <a href="https://docs.expo.io/workflow/debugging/#react-native-debugger" rel="external nofollow">React Native Debugger</a> سيزوّدك بنفس ميزات التنقيح، لكن لتطبيقات React Native.
</p>

<p>
	لنبدأ العمل بتثبيت منقِّح React Native بمساعدة <a href="https://github.com/jhen0409/react-native-debugger#installation" rel="external nofollow">تعليمات التثبيت.</a> شغّل المنقّح عند انتهاء التثبيت، وافتح نافذة تنقيح جديدة (يمكنك استخدام الاختصارات التالية: <code>command+T</code> على macOS و<code>Ctrl+T</code> على Windows/Linux)، ثم اضبط رقم منفذ محزِّم React Native على 19001.
</p>

<p>
	علينا الآن تشغيل تطبيقنا وربطه مع المنقّح. لننفذ إذًا الأمر <code>npm start</code> أولًا، ثم سنفتح التطبيق ضمن المقلّد أو تطبيق جوّال Expo. افتح قائمة المطوّرين داخل المقلّد أو التطبيق باتباعك <a href="https://docs.expo.io/workflow/debugging/#developer-menu" rel="external nofollow">الإرشادات</a> الموجودة في توثيق Expo. اختر من قائمة المطوّرين العنصر "Debug remote JS" لتتصل مع المنقّح. إن جرى كل شيء على مايرام سترى شجرة مكونات التطبيق ضمن المنقّح.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="71687" href="https://academy.hsoub.com/uploads/monthly_2021_07/app_component_tree_debug_07.png.25b558921e87651736a3e95a00c7e753.png" rel=""><img alt="app_component_tree_debug_07.png" class="ipsImage ipsImage_thumbnailed" data-fileid="71687" data-unique="j7hl0pofd" src="https://academy.hsoub.com/uploads/monthly_2021_07/app_component_tree_debug_07.png.25b558921e87651736a3e95a00c7e753.png"></a>
</p>

<p>
	يمكنك استخدام المنقّح للتحقق من حالة المكوّنات وخصائصها وتغييرها إن أردت. حاول أن تعثر مثلًا على المكوّن <code>Text</code> الذي يصيره المكوّن <code>App</code> باستخدام خيار البحث أو عبر شجرة المكوّنات. انقر المكوّن <code>text</code> وغّير قيمة الخاصية <code>children</code>. من المفترض أن يَظهر التغيير تلقائيًا في نافذة عرض التطبيق.
</p>

<p>
	للاطلاع على أدوات تنقيح أكثر فائدة لتطبيقات Expo، اطلع على <a href="https://docs.expo.io/workflow/debugging" rel="external nofollow">توثيق عملية التنقيح</a> في Expo.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://fullstackopen.com/en/part10/introduction_to_react_native" rel="external nofollow">Introduction to React Native</a> من سلسلة <a href="https://fullstackopen.com/en/" rel="external nofollow">Deep Dive Into Modern Web Development</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/typescript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-typescript-%D9%88%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%AA%D9%8A-%D8%AA%D9%88%D9%81%D8%B1%D9%87%D8%A7-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-react-r1218/" rel="">استخدام TypeScript والأنواع التي توفرها في تطبيقات React</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D8%AD%D8%B1%D9%8A%D9%83-%D9%81%D9%8A-react-native-r925/" rel="">مدخل إلى التحريك في React Native</a>.
	</li>
</ul>
]]></description><guid isPermaLink="false">1264</guid><pubDate>Thu, 08 Jul 2021 15:00:00 +0000</pubDate></item><item><title>&#x625;&#x62F;&#x627;&#x631;&#x629; &#x639;&#x645;&#x644;&#x64A;&#x629; &#x62A;&#x633;&#x62C;&#x64A;&#x644; &#x627;&#x644;&#x62F;&#x62E;&#x648;&#x644; &#x648;&#x62A;&#x62D;&#x62F;&#x64A;&#x62B; &#x627;&#x644;&#x630;&#x627;&#x643;&#x631;&#x629; &#x627;&#x644;&#x645;&#x624;&#x642;&#x62A;&#x629; &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642; React</title><link>https://academy.hsoub.com/programming/javascript/react/%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B3%D8%AC%D9%8A%D9%84-%D8%A7%D9%84%D8%AF%D8%AE%D9%88%D9%84-%D9%88%D8%AA%D8%AD%D8%AF%D9%8A%D8%AB-%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-r1212/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_04/608171f4128d4_----.png.02650ba4893400b4f76a3626d5b566e6.png" /></p>

<p>
	تُظهر الواجهة الأمامية لتطبيقنا محتويات دليل الهاتف بشكل جيد بمساعدة الخادم الذي يُحدَّث باستمرار. لكن إن أردنا إضافة أشخاص جدد، لابد من إضافة طريقة لتسجيل الدخول إلى الواجهة الأمامية.
</p>

<h2>
	تسجيل دخول المستخدم
</h2>

<p>
	لنضف المتغير <code>token</code> إلى حالة التطبيق. سيحتوي هذا المتغير على شهادة تحقق المستخدم عندما يسجل دخوله. فإن لم يكن المتغير <code>token</code> مُعرّفًا، سنُصيّر render المكوّن <code>LoginForm</code> المسؤول عن عملية تسجيل الدخول. سيتلقى المكوّن معاملين هما معالج خطأ والدالة <code>setToken</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2520_8" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">token</span><span class="pun">,</span><span class="pln"> setToken</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">)</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">token</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">Notify</span><span class="pln"> errorMessage</span><span class="pun">={</span><span class="pln">errorMessage</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="typ">Login</span><span class="pun">&lt;/</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="typ">LoginForm</span><span class="pln">
          setToken</span><span class="pun">={</span><span class="pln">setToken</span><span class="pun">}</span><span class="pln">
          setError</span><span class="pun">={</span><span class="pln">notify</span><span class="pun">}</span><span class="pln">
        </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنعرّف تاليًا طفرة لتسجيل الدخول:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2520_10" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> LOGIN </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  mutation login</span><span class="pun">(</span><span class="pln">$username</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!,</span><span class="pln"> $password</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    login</span><span class="pun">(</span><span class="pln">username</span><span class="pun">:</span><span class="pln"> $username</span><span class="pun">,</span><span class="pln"> password</span><span class="pun">:</span><span class="pln"> $password</span><span class="pun">)</span><span class="pln">  </span><span class="pun">{</span><span class="pln">
      value
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">`</span></pre>

<p>
	سيعمل المكوّن <code>LoginForm</code> بشكل مشابه لبقية المكوّنات التي أنشأناها سابقًا والتي تنفذ طفرات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2520_12" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useState</span><span class="pun">,</span><span class="pln"> useEffect </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useMutation </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> LOGIN </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../queries'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">LoginForm</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> setError</span><span class="pun">,</span><span class="pln"> setToken </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">username</span><span class="pun">,</span><span class="pln"> setUsername</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">password</span><span class="pun">,</span><span class="pln"> setPassword</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> login</span><span class="pun">,</span><span class="pln"> result </span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useMutation</span><span class="pun">(</span><span class="pln">LOGIN</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      onError</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      setError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">.</span><span class="pln">graphQLErrors</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">message</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">

  useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">data </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">const</span><span class="pln"> token </span><span class="pun">=</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">login</span><span class="pun">.</span><span class="pln">value
          setToken</span><span class="pun">(</span><span class="pln">token</span><span class="pun">)</span><span class="pln">
          localStorage</span><span class="pun">.</span><span class="pln">setItem</span><span class="pun">(</span><span class="str">'phonenumbers-user-token'</span><span class="pun">,</span><span class="pln"> token</span><span class="pun">)</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">},</span><span class="pln"> </span><span class="pun">[</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">])</span><span class="pln"> </span><span class="com">// ألغيت قاعدة المدقق لهذا السطر</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> submit </span><span class="pun">=</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    event</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">()</span><span class="pln">
    login</span><span class="pun">({</span><span class="pln"> variables</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> username</span><span class="pun">,</span><span class="pln"> password </span><span class="pun">}</span><span class="pln"> </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">form onSubmit</span><span class="pun">={</span><span class="pln">submit</span><span class="pun">}&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          username </span><span class="pun">&lt;</span><span class="pln">input
            value</span><span class="pun">={</span><span class="pln">username</span><span class="pun">}</span><span class="pln">
            onChange</span><span class="pun">={({</span><span class="pln"> target </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setUsername</span><span class="pun">(</span><span class="pln">target</span><span class="pun">.</span><span class="pln">value</span><span class="pun">)}</span><span class="pln">
          </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          password </span><span class="pun">&lt;</span><span class="pln">input
            type</span><span class="pun">=</span><span class="str">'password'</span><span class="pln">
            value</span><span class="pun">={</span><span class="pln">password</span><span class="pun">}</span><span class="pln">
            onChange</span><span class="pun">={({</span><span class="pln"> target </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setPassword</span><span class="pun">(</span><span class="pln">target</span><span class="pun">.</span><span class="pln">value</span><span class="pun">)}</span><span class="pln">
          </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">'submit'</span><span class="pun">&gt;</span><span class="pln">login</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">LoginForm</span></pre>

<p>
	لقد استعملنا خطاف التأثير effect-hook مجددًا. وقد استخدم لتخزين قيمة شهادة التحقق ضمن حالة المكوِّن <code>App</code> وضمن الذاكرة المحلية بعد أن يستجيب الخادم للطفرة. واستعمال خطاف التأثير ضروري لتلافي حلقات التصيير اللانهائية.
</p>

<p>
	لنضف أيضًا زرًَا لتسجيل خروج المسستخدم الذي سجّل دخوله. يغيّر معالج الحدث <code>onClick</code> الخاص بالزر قيمة قطعة الحالة <code>token</code> إلى null، كما يزيل شهادة التحقق المخزنة في الذاكرة المحلية، ويعيد ضبط الذاكرة المؤقتة الخاصة بالمكتبة Apollo client. إن هذه الخطوة الأخيرة <a data-ss1621334162="1" href="https://www.apollographql.com/docs/react/networking/authentication/#reset-store-on-logout" rel="external nofollow">مهمة</a> لأن بعض الاستعلامات قد تحضر بيانات إلى الذاكرة المؤقتة والتي لا يجب للمستخدم الوصول إليها قبل تسجيل دخوله.
</p>

<p>
	يمكن إعادة ضبط الذاكرة المؤقتة باستخدام التابع <a data-ss1621334162="1" href="https://www.apollographql.com/docs/react/v3.0-beta/api/core/ApolloClient/#ApolloClient.resetStore" rel="external nofollow">resetStore</a> العائد لكائن <code>ApolloClient</code>. ويمكن الوصول إلى هذا الكائن عبر الخطاف <a data-ss1621334162="1" href="https://www.apollographql.com/docs/react/api/react-hooks/#useapolloclient" rel="external nofollow">useApolloClient</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2520_14" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">token</span><span class="pun">,</span><span class="pln"> setToken</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">errorMessage</span><span class="pun">,</span><span class="pln"> setErrorMessage</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> useQuery</span><span class="pun">(</span><span class="pln">ALL_PERSONS</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> client </span><span class="pun">=</span><span class="pln"> useApolloClient</span><span class="pun">()</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">loading</span><span class="pun">)</span><span class="pln">  </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">loading</span><span class="pun">...&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> logout </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      setToken</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">)</span><span class="pln">
      localStorage</span><span class="pun">.</span><span class="pln">clear</span><span class="pun">()</span><span class="pln">
      client</span><span class="pun">.</span><span class="pln">resetStore</span><span class="pun">()</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكنك إيجاد شيفرة التطبيق بوضعه الحالي ضمن الفرع part8-6 في المستودع المخصص للتطبيق على <a data-ss1621334162="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-frontend/tree/part8-6" rel="external nofollow">GitHub</a>.
</p>

<h2>
	إضافة شهادة التحقق إلى الترويسة
</h2>

<p>
	بعد التغييرات التي طرأت على الواجهة الخلفية، سيتطلب إنشاء أشخاص جدد شهادة تحقق صالحة خاصة بالمستخدم عند إرسال الطلبات إلى الخادم. ولكي نرسل الشهادة، علينا تغيير الطريقة التي عرّفنا بها الكائن <code>ApolloClient</code> في الملف "index.js".
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2520_16" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> setContext </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'apollo-link-context'</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> authLink </span><span class="pun">=</span><span class="pln"> setContext</span><span class="pun">((</span><span class="pln">_</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> headers </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> token </span><span class="pun">=</span><span class="pln"> localStorage</span><span class="pun">.</span><span class="pln">getItem</span><span class="pun">(</span><span class="str">'phonenumbers-user-token'</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="pun">...</span><span class="pln">headers</span><span class="pun">,</span><span class="pln">
            authorization</span><span class="pun">:</span><span class="pln"> token </span><span class="pun">?</span><span class="pln"> </span><span class="pun">`</span><span class="pln">bearer $</span><span class="pun">{</span><span class="pln">token</span><span class="pun">}`</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}})</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> httpLink </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HttpLink</span><span class="pun">({</span><span class="pln"> uri</span><span class="pun">:</span><span class="pln"> </span><span class="str">'http://localhost:4000'</span><span class="pln"> </span><span class="pun">})</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> client </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ApolloClient</span><span class="pun">({</span><span class="pln">
  cache</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">InMemoryCache</span><span class="pun">(),</span><span class="pln">
  link</span><span class="pun">:</span><span class="pln"> authLink</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">httpLink</span><span class="pun">)})</span></pre>

<p>
	يحدد المعامل <code>link</code> الذي أُسند إلى الكائن <code>client</code> كيف تتصل Apollo مع الخادم. ولاحظ كيف عُدِّل اتصال <a data-ss1621334162="1" href="https://www.apollographql.com/docs/link/links/http.htm" rel="external nofollow">رابط HTTP</a> لكي تتضمن <a data-ss1621334162="1" href="https://www.apollographql.com/docs/react/networking/authentication/#header" rel="external nofollow">ترويسة التصريح</a> شهادة التحقق إن كانت مخزّنة في الذاكرة المحليّة.
</p>

<p>
	ولابدّ من تثبيت المكتبة اللازمة لهذا التعديل كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2520_18" style="">
<span class="pln">npm install apollo</span><span class="pun">-</span><span class="pln">link</span><span class="pun">-</span><span class="pln">context</span></pre>

<p>
	سنتمكن الآن من إضافة أشخاص جدد وتغيير الأرقام مجددًا. لكن لا تزال أمامنا مشكلة واحدة. فلو حاولنا إضافة شخص بلا رقم هاتف لن نستطيع ذلك.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64379" data-ss1621334162="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/error_person_without_phone_01.png.f8190b18a503b3f943c10aec07dcc586.png" rel=""><img alt="error_person_without_phone_01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64379" data-unique="ajwnte7bo" src="https://academy.hsoub.com/uploads/monthly_2021_04/error_person_without_phone_01.png.f8190b18a503b3f943c10aec07dcc586.png"></a>
</p>

<p>
	سيخفق تقييم البيانات، ذلك أن الواجهة الأمامية سترسل نصًا فارغًا للحقل <code>phone</code>. لنغيّر إذًا الدالة التي تنشئ الأشخاص الجدد لكي تعطي القيمة null للحقل <code>phone</code> إن لم يدخل المستخدم قيمة له.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2520_20" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">PersonForm</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> setError </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> submit </span><span class="pun">=</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    event</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">()</span><span class="pln">
    createPerson</span><span class="pun">({</span><span class="pln">
      variables</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> 
        name</span><span class="pun">,</span><span class="pln"> street</span><span class="pun">,</span><span class="pln"> city</span><span class="pun">,</span><span class="pln">
          phone</span><span class="pun">:</span><span class="pln"> phone</span><span class="pun">.</span><span class="pln">length </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> phone </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">null</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">

  </span><span class="com">// ...</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكنك إيجاد شيفرة التطبيق بوضعه الحالي ضمن الفرع part8-7 في المستودع المخصص للتطبيق على <a data-ss1621334162="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-frontend/tree/part8-7" rel="external nofollow">GitHub</a>.
</p>

<h2>
	تحديث الذاكرة المؤقتة (مرور ثان)
</h2>

<p>
	ينبغي علينا <a data-ss1621334162="1" href="https://fullstackopen.com/en/part8/react_and_graph_ql#updating-the-cache" rel="external nofollow">تحديث</a> الذاكرة المؤقتة للمكتبة Apollo client عند إنشاء أشخاص جدد. ويمكننا ذلك باستخدام الخيار <code>refetchQueries</code> للطفرة والذي يسمح بتنفيذ الاستعلام <code>ALL_PERSONS</code> مرة أخرى.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2520_23" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">PersonForm</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> setError </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> createPerson </span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useMutation</span><span class="pun">(</span><span class="pln">CREATE_PERSON</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    refetchQueries</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">  </span><span class="pun">{</span><span class="pln">query</span><span class="pun">:</span><span class="pln"> ALL_PERSONS</span><span class="pun">}</span><span class="pln"> </span><span class="pun">],</span><span class="pln">
      onError</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      setError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">.</span><span class="pln">graphQLErrors</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">message</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">})</span></pre>

<p>
	يعتبر ما فعلناه سابقًا مقاربة جيدة، لكن العقبة التي ستعترضنا هي أن الاستعلام سيُنفّذ من جديد عند أي تحديث. يمكن استمثال الحل بمعالجة موضوع التحديث بأنفسنا، وذلك بتعريف <a data-ss1621334162="1" href="https://www.apollographql.com/docs/react/v3.0-beta/api/react/hooks/#options" rel="external nofollow">دالة استدعاء للتحديث</a> خاصة بالطفرة، بحيث تستدعيها المكتبة Apollo بعد الطفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2520_26" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">PersonForm</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> setError </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> createPerson </span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useMutation</span><span class="pun">(</span><span class="pln">CREATE_PERSON</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    onError</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      setError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">.</span><span class="pln">graphQLErrors</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">message</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    update</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">store</span><span class="pun">,</span><span class="pln"> response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">const</span><span class="pln"> dataInStore </span><span class="pun">=</span><span class="pln"> store</span><span class="pun">.</span><span class="pln">readQuery</span><span class="pun">({</span><span class="pln"> query</span><span class="pun">:</span><span class="pln"> ALL_PERSONS </span><span class="pun">})</span><span class="pln">      store</span><span class="pun">.</span><span class="pln">writeQuery</span><span class="pun">({</span><span class="pln">
            query</span><span class="pun">:</span><span class="pln"> ALL_PERSONS</span><span class="pun">,</span><span class="pln">
            data</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="pun">...</span><span class="pln">dataInStore</span><span class="pun">,</span><span class="pln">
                allPersons</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> </span><span class="pun">...</span><span class="pln">dataInStore</span><span class="pun">.</span><span class="pln">allPersons</span><span class="pun">,</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">addPerson </span><span class="pun">]</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">

  </span><span class="com">// ..</span><span class="pln">
</span><span class="pun">}</span><span class="pln">  </span></pre>

<p>
	تُعطى دالة الاستدعاء دلالة مرجعية إلى الذاكرة المؤقتة وإلى البيانات التي تعيدها الطفرة على شكل معاملات. في حالتنا على سبيل المثال، عند إنشاء شخص جديد ستقرأ الشيفرة حالة الذاكرة المؤقتة للاستعلام <code>ALL_PERSONS</code> باستخدام الدالة <a data-ss1621334162="1" href="https://www.apollographql.com/docs/react/v3.0-beta/caching/cache-interaction/#readquery" rel="external nofollow">readQuery</a>، وستحدث الذاكرة المؤقتة باستخدام الدالة <a data-ss1621334162="1" href="https://www.apollographql.com/docs/react/v3.0-beta/caching/cache-interaction/#writequery-and-writefragment" rel="external nofollow">writeQuery</a> التي ستضيف الشخص الجديد إليها.
</p>

<p>
	انتبه إلى الدالة <code>readQuery</code> التي ستعطي خطأً إن لم تحتوي الذاكرة المؤقتة على كل البيانات التي تلبي متطلبات الاستعلام. يمكن التقاط الخطأ باستخدام الكتلة try/catch.
</p>

<p>
	إنّ الحل الأكثر منطقية لتحديث الذاكرة المؤقتة في بعض الحالات هو استخدام <a data-ss1621334162="1" href="https://www.apollographql.com/docs/react/v3.0-beta/api/react/hooks/#options" rel="external nofollow">دالة استدعاء للتحديث</a>.
</p>

<p>
	يمكن عند الضرورة تعطيل الذاكرة المؤقتة للتطبيق ككل أو <a data-ss1621334162="1" href="https://www.apollographql.com/docs/react/api/react/hooks/#options" rel="external nofollow">لاستعلامات مفردة</a> بضبط الحقل <a data-ss1621334162="1" href="https://www.apollographql.com/docs/react/data/queries/#configuring-fetch-logic" rel="external nofollow">fetchPolicy</a> الذي يدير الذاكرة المؤقتة على القيمة no-cache.
</p>

<p>
	انتبه دائمًا للذاكرة المؤقتة، فالبيانات القديمة فيها قد تسبب ثغرات صعبة الإيجاد. وكما نعرف إن الحفاظ على الحالة المحدّثة للذاكرة المؤقتة أمر صعب و نستشف ذلك من المقولة التالية لأحد المبرمجين:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		ليس هناك سوى أمرين شاقين فقط في علوم الحاسب: مشاكل الذاكرة المؤقتة وتسمية الأشياء.
	</p>
</blockquote>

<p>
	يمكنك إيجاد شيفرة التطبيق بوضعه الحالي ضمن الفرع part8-8 في المستودع المخصص للتطبيق على <a data-ss1621334162="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-frontend/tree/part8-8" rel="external nofollow">GitHub</a>.
</p>

<h2>
	التمارين
</h2>

<h3>
	1. إنشاء قائمة كتب
</h3>

<p>
	لن تعمل قائمة الكتب بعد التغييرات التي أجريناها على الواجهة الخلفية، لذا جد حلًا.
</p>

<h3>
	2. تسجيل الدخول
</h3>

<p>
	لن تعمل وظيفة إضافة كتب جديدة، ولا تغيير عام ميلاد المؤلف لأنها تتطلب تسجيل دخول المستخدِم. أضف وظيفة تسجيل الدخول وأصلح الطفرات، ولا حاجة الآن لتعالج أخطاء التقييم.
</p>

<p>
	يمكن أن تقرر الطريقة التي ستعرض بها واجهة تسجيل الدخول، ومن بين الحلول المقترحة هو وضع نموذج التسجيل ضمن واجهة منفصلة يمكن الوصول إليها عبر قائمة من خيارات التنقل.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64383" data-ss1621334162="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/login_form_nav_menu_02.png.01d047c937459ac3094be81085edaf25.png" rel=""><img alt="login_form_nav_menu_02.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64383" data-unique="snvwnaoi9" src="https://academy.hsoub.com/uploads/monthly_2021_04/login_form_nav_menu_02.png.01d047c937459ac3094be81085edaf25.png"></a>
</p>

<p>
	نموذج تسجيل الدخول:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64384" data-ss1621334162="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/login_view_03.png.6062f33423ae5330f00dea4dd2f7b52d.png" rel=""><img alt="login_view_03.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64384" data-unique="z3todjggp" src="https://academy.hsoub.com/uploads/monthly_2021_04/login_view_03.png.6062f33423ae5330f00dea4dd2f7b52d.png"></a>
</p>

<p>
	عندما يسجل المستخدم دخوله، تتغير قائمة التنقل لإظهار الوظائف التي ينفذها التطبيق فقط عندما يسجل المستخدم دخوله:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64382" data-ss1621334162="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/loggedin_functions_04.png.4fc9a750650c3bf78f4f58f5f3b27e76.png" rel=""><img alt="loggedin_functions_04.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64382" data-unique="7vkm1n63r" src="https://academy.hsoub.com/uploads/monthly_2021_04/loggedin_functions_04.png.4fc9a750650c3bf78f4f58f5f3b27e76.png"></a>
</p>

<h3>
	3. اختيار الكتب بناء على نوعها: القسم الأول
</h3>

<p>
	أكمل التطبيق بانتقاء الكتب بناء على نوعها. قد يبدو الحل كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64380" data-ss1621334162="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/filter_books_by_genre_05.png.df488c169400d35e610285d2780a8387.png" rel=""><img alt="filter_books_by_genre_05.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64380" data-unique="yh9cj774m" src="https://academy.hsoub.com/uploads/monthly_2021_04/filter_books_by_genre_05.png.df488c169400d35e610285d2780a8387.png"></a>
</p>

<p>
	يمكن إنجاز عملية الانتقاء في هذا التمرين باستخدام React فقط.
</p>

<h3>
	4. اختيار الكتب بناء على نوعها: القسم الثاني
</h3>

<p>
	نفذ آلية لإظهار كل الكتب من النوع المفضل للمستخدم عند تسجيل دخوله.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64381" data-ss1621334162="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/filter_by_user_favorites_06.png.045a36a079ba554002a8e0b372cdcec0.png" rel=""><img alt="filter_by_user_favorites_06.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64381" data-unique="hxlpg3x2v" src="https://academy.hsoub.com/uploads/monthly_2021_04/filter_by_user_favorites_06.png.045a36a079ba554002a8e0b372cdcec0.png"></a>
</p>

<h3>
	5. اختار الكتب بناء على نوعها باستخدام GraphQL
</h3>

<p>
	أمكننا إنجاز عملية الانتقاء في التمرين السابق باستخدام React. لإتمام هذا التمرين، ينبغي عليك انتقاء الكتب في صفحة التوصيات بإرسال استعلام GraphQ إلى الخادم.
</p>

<p>
	يمثل هذا التمرين والتمرين الذي يليه تحديًا حقيقيًا كما هو المفترض في هذه المرحلة من المنهاج. ربما عليك إكمال التمارين الأسهل أولًا والموجودة في القسم التالي.
</p>

<p>
	هذه بعض النصائح:
</p>

<ul>
<li>
		قد يكون من الأفضل استخدام الخطاف <code>useLazyQuery</code> في الاستعلامات بدلًا من <code>useQuery</code>.
	</li>
	<li>
		من المفيد في بعض الأحيان تخزين نتيجة استعلام GraphQL ضمن حالة المكوِّن.
	</li>
	<li>
		انتبه إلى إمكانية إنجاز استعلامات GraphQL باستخدام الخطاف <code>useEffect</code>.
	</li>
	<li>
		يمكن أن يساعدك <a data-ss1621334162="1" href="https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect" rel="external nofollow">المعامل الثاني</a> للخطاف <code>useEffect</code>، وذلك بناء على المقاربة التي ستسلكها في الحل.
	</li>
</ul>
<h3>
	6. ذاكرة مؤقتة محدثة والكتب الموصى بها
</h3>

<p>
	إن أحضرت الكتب الموصى بها مستخدمًا GraphQL، تأكد من أنّ واجهة عرض الكتب ستبقى محدّثة بشكل أو بآخر. فعندما يُضاف كتاب جديد، لابدّ من تحديث واجهة عرض الكتب على الأقل عند الضغط على زر اختيار نوع الكتاب، وإن لم تختر نوعًا محددًا من الكتب، فلا حاجة إذًا لتحديث واجهة عرض الكتب.
</p>

<p>
	ترجمة -وبتصرف- للفصل Login and Updating the cache من سلسلة <a data-ss1621334162="1" href="https://fullstackopen.com/en/" rel="external nofollow">Deep Dive Into Modern Web Development</a>
</p>
]]></description><guid isPermaLink="false">1212</guid><pubDate>Thu, 06 May 2021 09:08:00 +0000</pubDate></item><item><title>&#x646;&#x645;&#x648;&#x630;&#x62C; &#x639;&#x646; &#x628;&#x646;&#x627;&#x621; &#x642;&#x648;&#x627;&#x639;&#x62F; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x648;&#x625;&#x62F;&#x627;&#x631;&#x629; &#x627;&#x644;&#x645;&#x633;&#x62A;&#x62E;&#x62F;&#x645;&#x64A;&#x646; &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642; &#x648;&#x64A;&#x628;</title><link>https://academy.hsoub.com/programming/javascript/react/%D9%86%D9%85%D9%88%D8%B0%D8%AC-%D8%B9%D9%86-%D8%A8%D9%86%D8%A7%D8%A1-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D9%8A%D9%86-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-r1211/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_04/60816bd4a1dba_---.png.7ef9a0c3b7f36eceb370baf44689a7e7.png" /></p>

<p>
	سنضيف في هذا المقال آليةً لإدارة المستخدمين في تطبيقنا، لكن دعونا أولًا نستخدم قاعدة بيانات لتخزين بيانات التطبيق.
</p>

<h2>
	استخدام المكتبة Mongoose مع المكتبة Apollo
</h2>

<p>
	ثبِّت المكتبتين Mongoose، وMongoose-unique-validator كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_7" style="">
<span class="pln">npm install mongoose mongoose</span><span class="pun">-</span><span class="pln">unique</span><span class="pun">-</span><span class="pln">validator</span></pre>

<p>
	سنقلّد ما فعلناه في القسمين 3، و4.
</p>

<p>
	لقد عرّفنا سابقًا تخطيط الأشخاص كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_9" style="">
<span class="kwd">const</span><span class="pln"> mongoose </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'mongoose'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> schema </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> mongoose</span><span class="pun">.</span><span class="typ">Schema</span><span class="pun">({</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    type</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    required</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    unique</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    minlength</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  phone</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    type</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    minlength</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  street</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    type</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    required</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    minlength</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  city</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    type</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    required</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    minlength</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> mongoose</span><span class="pun">.</span><span class="pln">model</span><span class="pun">(</span><span class="str">'Person'</span><span class="pun">,</span><span class="pln"> schema</span><span class="pun">)</span></pre>

<p>
	لقد أضفنا أيضًا عدة مقيّمات validator، وهي <code>required:true</code> التي تتحقق من أنّ القيمة موجودة، وطبعًا لا حاجة فعلية لهذا المقيّم لأن GraphQL تتأكد من وجود الحقل تلقائيًا. لكن بالطبع من الجيد وجود مقيّمات في قاعدة البيانات.
</p>

<p>
	يمكن تشغيل التطبيق ليعمل عمومًا، بتنفيذ التعديلات التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_11" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ApolloServer</span><span class="pun">,</span><span class="pln"> </span><span class="typ">UserInputError</span><span class="pun">,</span><span class="pln"> gql </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'apollo-server'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> mongoose </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'mongoose'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Person</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'./models/person'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> MONGODB_URI </span><span class="pun">=</span><span class="pln"> </span><span class="str">'mongodb+srv://fullstack:halfstack@cluster0-ostce.mongodb.net/graphql?retryWrites=true'</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'connecting to'</span><span class="pun">,</span><span class="pln"> MONGODB_URI</span><span class="pun">)</span><span class="pln">

mongoose</span><span class="pun">.</span><span class="pln">connect</span><span class="pun">(</span><span class="pln">MONGODB_URI</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useNewUrlParser</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> useUnifiedTopology</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> useFindAndModify</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln"> useCreateIndex</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'connected to MongoDB'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">((</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'error connection to MongoDB:'</span><span class="pun">,</span><span class="pln"> error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> typeDefs </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
</span><span class="pun">`</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> resolvers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">Query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    personCount</span><span class="pun">:</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">.</span><span class="pln">collection</span><span class="pun">.</span><span class="pln">countDocuments</span><span class="pun">(),</span><span class="pln">
    allPersons</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// filters missing</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">.</span><span class="pln">find</span><span class="pun">({})</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    findPerson</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">.</span><span class="pln">findOne</span><span class="pun">({</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">name </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="typ">Person</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    address</span><span class="pun">:</span><span class="pln"> root </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        street</span><span class="pun">:</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">street</span><span class="pun">,</span><span class="pln">
        city</span><span class="pun">:</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">city
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  </span><span class="typ">Mutation</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    addPerson</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> person </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">({</span><span class="pln"> </span><span class="pun">...</span><span class="pln">args </span><span class="pun">})</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> person</span><span class="pun">.</span><span class="pln">save</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    editNumber</span><span class="pun">:</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> person </span><span class="pun">=</span><span class="pln"> await </span><span class="typ">Person</span><span class="pun">.</span><span class="pln">findOne</span><span class="pun">({</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">name </span><span class="pun">})</span><span class="pln">
      person</span><span class="pun">.</span><span class="pln">phone </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">phone
      </span><span class="kwd">return</span><span class="pln"> person</span><span class="pun">.</span><span class="pln">save</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تُعَدّ التعديلات التي أجريت واضحة تمامًا. لكن سنشير إلى عدة نقاط تستحق الوقوف عندها. كما نتذكر، فإنّ الحقل المُعرِّف لكائن في Mongo يدعى "id_". ولقد كان علينا سابقًا تحويل اسم الحقل إلى المعرّف id بأنفسنا، لكن ستنفّذ لنا المكتبة GraphQL الآن هذا الأمر تلقائيًا؛ أما الملاحظة الأخرى الجديرة بالاهتمام هي أنّ دوال المحللات ستعيد وعودًا promises، وقد كانت تعيد سابقًا كائنات. فعندما يعيد المحلل وعدًا، <a href="https://www.apollographql.com/docs/apollo-server/data/data/#resolver-results" rel="external nofollow">سيُعيد خادم Apollo</a> القيمة التي يشير إليها الوعد.
</p>

<p>
	فلو نُفِّذت على سبيل المثال دالة المحلل التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_13" style="">
<span class="pln">allPersons</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">.</span><span class="pln">find</span><span class="pun">({})</span><span class="pln">
</span><span class="pun">},</span></pre>

<p>
	سينتظر خادم Apollo حتى تجري معالجة الوعد، ومن ثم يعيد النتيجة. إذًا فالمكتبة Apollo تعمل تقريبيًا كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_15" style="">
<span class="typ">Person</span><span class="pun">.</span><span class="pln">find</span><span class="pun">({}).</span><span class="pln">then</span><span class="pun">(</span><span class="pln"> result </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// يعيد النتيجة </span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	لنكمل كتابة المحلل <code>allPersons</code> لكي يأخذ المعامل <code>phone</code> في الحسبان:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_17" style="">
<span class="typ">Query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ..</span><span class="pln">
  allPersons</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">args</span><span class="pun">.</span><span class="pln">phone</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">.</span><span class="pln">find</span><span class="pun">({})</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">.</span><span class="pln">find</span><span class="pun">({</span><span class="pln"> phone</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> $exists</span><span class="pun">:</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">phone </span><span class="pun">===</span><span class="pln"> </span><span class="str">'YES'</span><span class="pln">  </span><span class="pun">}})</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">},</span></pre>

<p>
	فلو لم يمتلك الاستعلام المعامل <code>phone</code>، ستعيد الاستجابة كل الأشخاص. أما إن امتلك المعامل القيمة YES، ستعيد نتيجة الاستعلام الكائنات التي يمتلك فيها الحقل <code>phone</code> قيمة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_19" style="">
<span class="typ">Person</span><span class="pun">.</span><span class="pln">find</span><span class="pun">({</span><span class="pln"> phone</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> $exists</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">}})</span></pre>

<p>
	وفي حال امتلك المعامل القيمة NO، سيعيد الاستعلام الكائنات التي لا يمتلك فيها الحقل <code>phone</code> قيمة:
</p>

<pre class="ipsCode">
Person.find({ phone: { $exists: false }})
</pre>

<h2>
	تقييم صحة البيانات
</h2>

<p>
	كما هي الحال في GraphQL، سيتم تقييم صحة البيانات المدخلة باستخدام المقيّمات المعرّفة ضمن تخطيط Mongoose. وللتعامل مع الأخطاء الناتجة عن تقييم البيانات في التخطيط، لا بد من إضافة معالجات أخطاء على شكل كتل try/catch إلى التابع <code>save</code>. وفي حال وصل تنفيذ الشيفرة إلى الجزء catch، سنظهر عندها الاستثناء المناسب.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_21" style="">
<span class="typ">Mutation</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  addPerson</span><span class="pun">:</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> person </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">({</span><span class="pln"> </span><span class="pun">...</span><span class="pln">args </span><span class="pun">})</span><span class="pln">

      </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        await person</span><span class="pun">.</span><span class="pln">save</span><span class="pun">()</span><span class="pln">
      </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">UserInputError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          invalidArgs</span><span class="pun">:</span><span class="pln"> args</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">})</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> person
  </span><span class="pun">},</span><span class="pln">
    editNumber</span><span class="pun">:</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> person </span><span class="pun">=</span><span class="pln"> await </span><span class="typ">Person</span><span class="pun">.</span><span class="pln">findOne</span><span class="pun">({</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">name </span><span class="pun">})</span><span class="pln">
      person</span><span class="pun">.</span><span class="pln">phone </span><span class="pun">=</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">phone

      </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        await person</span><span class="pun">.</span><span class="pln">save</span><span class="pun">()</span><span class="pln">
      </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">UserInputError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          invalidArgs</span><span class="pun">:</span><span class="pln"> args</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">})</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> person
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستجد شيفرة الواجهة الخلفية ضمن الفرع part8-4 في المستودع المخصص للتطبيق على <a data-ss1621334060="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-backend/tree/part8-4" rel="external nofollow">Github</a>.
</p>

<h2>
	تسجيل دخول المستخدمين
</h2>

<p>
	سنضيف الآن آلية لإدارة المستخدمين في تطبيقنا. وتوخيًا للبساطة، سنفترض أن لجميع المستخدمين كلمة المرور نفسها وسنكتبها مسبقًا ضمن الشيفرة. سيكون تخزين كلمات السر الخاصة بكل مستخدم مباشرًا وفق المبدأ الذي اتبعناه في القسم 4، لكن طالما أنّ تركيزنا سينصب على GraphQL فسنترك بقية التفاصيل حتى يحين وقتها.
</p>

<p>
	سيكون التخطيط الخاص بالمستخدِم على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_23" style="">
<span class="kwd">const</span><span class="pln"> mongoose </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'mongoose'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> schema </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> mongoose</span><span class="pun">.</span><span class="typ">Schema</span><span class="pun">({</span><span class="pln">
  username</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    type</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    required</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    unique</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    minlength</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  friends</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
    </span><span class="pun">{</span><span class="pln">
      type</span><span class="pun">:</span><span class="pln"> mongoose</span><span class="pun">.</span><span class="typ">Schema</span><span class="pun">.</span><span class="typ">Types</span><span class="pun">.</span><span class="typ">ObjectId</span><span class="pun">,</span><span class="pln">
      ref</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Person'</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">],</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> mongoose</span><span class="pun">.</span><span class="pln">model</span><span class="pun">(</span><span class="str">'User'</span><span class="pun">,</span><span class="pln"> schema</span><span class="pun">)</span></pre>

<p>
	يرتبط كل مستخدم بمجموعة من الأشخاص الموجودين في النظام عن طريق الحقل <code>friends</code>. الغاية من ذلك أنه عندما يضيف مستخدم ما mluukkai على سبيل المثال شخصًا آخر وليكن Arto Hellas إلى القائمة فسيضاف هذا الشخص إلى قائمة الأصدقاء. وهكذا سيكون لكل مستخدم عند تسجيل دخوله واجهة عرض خاصة به في التطبيق.
</p>

<p>
	أما عن الآلية التي تتعامل مع تسجيل الدخول والتحقق من المستخدمين فهي نفسها التي اعتمدناها في القسم 4 عندما استخدمنا REST وشهادات التحقق.
</p>

<p>
	لنوسّع الآن التخطيط ليصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_25" style="">
<span class="pln">type </span><span class="typ">User</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  username</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
  friends</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Person</span><span class="pun">!]!</span><span class="pln">
  id</span><span class="pun">:</span><span class="pln"> ID</span><span class="pun">!</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

type </span><span class="typ">Token</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  value</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

type </span><span class="typ">Query</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ..</span><span class="pln">
  me</span><span class="pun">:</span><span class="pln"> </span><span class="typ">User</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

type </span><span class="typ">Mutation</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  createUser</span><span class="pun">(</span><span class="pln">
    username</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
  </span><span class="pun">):</span><span class="pln"> </span><span class="typ">User</span><span class="pln">
  login</span><span class="pun">(</span><span class="pln">
    username</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
    password</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
  </span><span class="pun">):</span><span class="pln"> </span><span class="typ">Token</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيعيد الاستعلام <code>me</code> معلومات المستخدم الذي سجّل دخوله. وتجري إضافة مستخدمين جدد باستخدام الطفرة <code>createUser</code>، كما يجري تسجيل الدخول باستخدام الطفرة <code>login</code>.
</p>

<p>
	تمثل الشيفرة التالية شيفرة المحللات للطفرات التي سنستخدمها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_27" style="">
<span class="kwd">const</span><span class="pln"> jwt </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'jsonwebtoken'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> JWT_SECRET </span><span class="pun">=</span><span class="pln"> </span><span class="str">'NEED_HERE_A_SECRET_KEY'</span><span class="pln">

</span><span class="typ">Mutation</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ..</span><span class="pln">
  createUser</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> user </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">User</span><span class="pun">({</span><span class="pln"> username</span><span class="pun">:</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">username </span><span class="pun">})</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> user</span><span class="pun">.</span><span class="pln">save</span><span class="pun">()</span><span class="pln">
      </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">error </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">UserInputError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          invalidArgs</span><span class="pun">:</span><span class="pln"> args</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">})</span><span class="pln">
      </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  login</span><span class="pun">:</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> user </span><span class="pun">=</span><span class="pln"> await </span><span class="typ">User</span><span class="pun">.</span><span class="pln">findOne</span><span class="pun">({</span><span class="pln"> username</span><span class="pun">:</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">username </span><span class="pun">})</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="pun">!</span><span class="pln">user </span><span class="pun">||</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">password </span><span class="pun">!==</span><span class="pln"> </span><span class="str">'secred'</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">UserInputError</span><span class="pun">(</span><span class="str">"wrong credentials"</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">const</span><span class="pln"> userForToken </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      username</span><span class="pun">:</span><span class="pln"> user</span><span class="pun">.</span><span class="pln">username</span><span class="pun">,</span><span class="pln">
      id</span><span class="pun">:</span><span class="pln"> user</span><span class="pun">.</span><span class="pln">_id</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> value</span><span class="pun">:</span><span class="pln"> jwt</span><span class="pun">.</span><span class="pln">sign</span><span class="pun">(</span><span class="pln">userForToken</span><span class="pun">,</span><span class="pln"> JWT_SECRET</span><span class="pun">)</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">},</span></pre>

<p>
	تُنفّذ الطفرة التي تسجّل مستخدِم جديد بطريقة مباشرة. حيث تتحقق طفرة تسجيل الدخول إن كان الزوج اسم مستخدم/كلمة مرور صالحين، فإن كان كذلك ستعيد شهادة تحقق من النوع "JSON-WEB-TOKEN" والتي تعاملنا معها في القسم 4.
</p>

<p>
	وتتكرر الفكرة السابقة عندما استخدمنا REST. حيث يحصل المستخدم الذي سجل دخوله على شهادة تحقق يضيفها إلى جميع الطلبات التي سيرسلها إلى الخادم. ستضاف أيضًا شهادة التحقق إلى استعلامات GraphQL باستخدام الترويسة Authorization.
</p>

<p>
	تُضاف الترويسة في أرضية عمل GraphQL إلى الاستعلام كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64376" data-ss1621334060="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/add_auth_header_grapql_01.png.e4700041f941c170f42532533768effe.png" rel=""><img alt="add_auth_header_grapql_01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64376" data-unique="cy6pq3a7r" src="https://academy.hsoub.com/uploads/monthly_2021_04/add_auth_header_grapql_01.png.e4700041f941c170f42532533768effe.png"></a>
</p>

<p>
	لنوسّع الآن تعريف الكائن<code>server</code> بإضافة معامل ثالث هو <a data-ss1621334060="1" href="https://www.apollographql.com/docs/apollo-server/data/data/#context-argument" rel="external nofollow">context</a> العائد لدوال المحللات إلى استدعاء الدالة البانية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_29" style="">
<span class="kwd">const</span><span class="pln"> server </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ApolloServer</span><span class="pun">({</span><span class="pln">
  typeDefs</span><span class="pun">,</span><span class="pln">
  resolvers</span><span class="pun">,</span><span class="pln">
  context</span><span class="pun">:</span><span class="pln"> async </span><span class="pun">({</span><span class="pln"> req </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> auth </span><span class="pun">=</span><span class="pln"> req </span><span class="pun">?</span><span class="pln"> req</span><span class="pun">.</span><span class="pln">headers</span><span class="pun">.</span><span class="pln">authorization </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">null</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">auth </span><span class="pun">&amp;&amp;</span><span class="pln"> auth</span><span class="pun">.</span><span class="pln">toLowerCase</span><span class="pun">().</span><span class="pln">startsWith</span><span class="pun">(</span><span class="str">'bearer '</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">const</span><span class="pln"> decodedToken </span><span class="pun">=</span><span class="pln"> jwt</span><span class="pun">.</span><span class="pln">verify</span><span class="pun">(</span><span class="pln">
              auth</span><span class="pun">.</span><span class="pln">substring</span><span class="pun">(</span><span class="lit">7</span><span class="pun">),</span><span class="pln"> JWT_SECRET
          </span><span class="pun">)</span><span class="pln">
          </span><span class="kwd">const</span><span class="pln"> currentUser </span><span class="pun">=</span><span class="pln"> await </span><span class="typ">User</span><span class="pun">.</span><span class="pln">findById</span><span class="pun">(</span><span class="pln">decodedToken</span><span class="pun">.</span><span class="pln">id</span><span class="pun">).</span><span class="pln">populate</span><span class="pun">(</span><span class="str">'friends'</span><span class="pun">)</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> currentUser </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}})</span></pre>

<p>
	يوزَّع الكائن الذي يعيده Context إلى جميع محللات الطفرات على أساس معامل ثالث. يعتبر تمرير الأشياء إلى المحللات بالاستعانة بالمعامل Context مثاليًا، وخاصةً إذا كانت مشتركةً بين عدة محللات مثل <a data-ss1621334060="1" href="https://blog.apollographql.com/authorization-in-graphql-452b1c402a9?_ga=2.45656161.474875091.1550613879-1581139173.1549828167" rel="external nofollow">التحقق من المستخدمين.</a>
</p>

<p>
	يَعُدّ محلل الطفرة <code>me</code> بسيطًا، فهو يعيد فقط المستخدِم الذي سجل دخوله والذي يحصل عليه من الحقل <code>currentUser</code> للمعامل context لدالة المحلل. لكن إن لم يسجّل أي مستخدم دخوله، أي لا وجود لشهادة تحقق صحيحة مرفقة مع الطلب، سيعيد الاستعلام القيمة null:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_32" style="">
<span class="typ">Query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  me</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">,</span><span class="pln"> context</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> context</span><span class="pun">.</span><span class="pln">currentUser
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">},</span></pre>

<h2>
	قائمة الأصدقاء
</h2>

<p>
	لنكمل الواجهة الخلفية للتطبيق، بحيث يحتاج إضافة أو تعديل معلومات الشخص تسجيل دخول، وكذلك سنضيف الأشخاص الذين يضيفهم المستخدم تلقائيًا إلى قائمة أصدقائه.
</p>

<p>
	لنزل أولًا كل الأشخاص الذين لا يتواجدون ضمن قائمة أصدقاء أي مستخدم في قاعدة البيانات. سنغير الطفرة <code>addPerson</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_34" style="">
<span class="typ">Mutation</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  addPerson</span><span class="pun">:</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">,</span><span class="pln"> context</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> person </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">({</span><span class="pln"> </span><span class="pun">...</span><span class="pln">args </span><span class="pun">})</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> currentUser </span><span class="pun">=</span><span class="pln"> context</span><span class="pun">.</span><span class="pln">currentUser

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">currentUser</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AuthenticationError</span><span class="pun">(</span><span class="str">"not authenticated"</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      await person</span><span class="pun">.</span><span class="pln">save</span><span class="pun">()</span><span class="pln">
      currentUser</span><span class="pun">.</span><span class="pln">friends </span><span class="pun">=</span><span class="pln"> currentUser</span><span class="pun">.</span><span class="pln">friends</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">person</span><span class="pun">)</span><span class="pln">
      await currentUser</span><span class="pun">.</span><span class="pln">save</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">UserInputError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        invalidArgs</span><span class="pun">:</span><span class="pln"> args</span><span class="pun">,</span><span class="pln">
      </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> person
  </span><span class="pun">},</span><span class="pln">
  </span><span class="com">//...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إن لم نستطع الحصول على تفاصيل المستخدم الذي سجل دخوله من خلال المعامل context، سيُرمى استثناء.
</p>

<p>
	يُضاف الشخص الجديد الآن من خلال العبارة async/await لأن الشخص الذي سيُضاف، سيوضع ضمن قائمة أصدقاء المستخدم في حال تمت عملية الإضافة بنجاح.
</p>

<p>
	لنضف آلية لإضافة مستخدمين موجودين إلى قائمة الأصدقاء. ستكون الطفرة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_36" style="">
<span class="pln">type </span><span class="typ">Mutation</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  addAsFriend</span><span class="pun">(</span><span class="pln">
    name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
  </span><span class="pun">):</span><span class="pln"> </span><span class="typ">User</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وستكون دالة المحلل الخاص بالطفرة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_38" style="">
<span class="pln">  addAsFriend</span><span class="pun">:</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> currentUser </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> nonFriendAlready </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">person</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> 
      </span><span class="pun">!</span><span class="pln">currentUser</span><span class="pun">.</span><span class="pln">friends</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">f </span><span class="pun">=&gt;</span><span class="pln"> f</span><span class="pun">.</span><span class="pln">_id</span><span class="pun">).</span><span class="pln">includes</span><span class="pun">(</span><span class="pln">person</span><span class="pun">.</span><span class="pln">_id</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">currentUser</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AuthenticationError</span><span class="pun">(</span><span class="str">"not authenticated"</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">const</span><span class="pln"> person </span><span class="pun">=</span><span class="pln"> await </span><span class="typ">Person</span><span class="pun">.</span><span class="pln">findOne</span><span class="pun">({</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">name </span><span class="pun">})</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> nonFriendAlready</span><span class="pun">(</span><span class="pln">person</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      currentUser</span><span class="pun">.</span><span class="pln">friends </span><span class="pun">=</span><span class="pln"> currentUser</span><span class="pun">.</span><span class="pln">friends</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">person</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    await currentUser</span><span class="pun">.</span><span class="pln">save</span><span class="pun">()</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> currentUser
  </span><span class="pun">},</span></pre>

<p>
	لاحظ كيف يفكك المحلل resolver معلومات المستخدم الذي يسجل دخوله والتي يؤمنها المعامل context. لذلك وبدلًا من تخزين محتويات الحقل <code>currentUser</code> ضمن متغير منفصل ضمن الدالة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_40" style="">
<span class="pln">addAsFriend</span><span class="pun">:</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">,</span><span class="pln"> context</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> currentUser </span><span class="pun">=</span><span class="pln"> context</span><span class="pun">.</span><span class="pln">currentUser</span></pre>

<p>
	ستمرر مباشرةً ضمن تعريف المعامل في الدالة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_43" style="">
<span class="pln">addAsFriend</span><span class="pun">:</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> currentUser </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span></pre>

<p>
	ستجد شيفرة الواجهة الخلفية ضمن الفرع part8-5 في المستودع المخصص للتطبيق على <a data-ss1621334060="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-backend/tree/part8-5" rel="external nofollow">Github</a>.
</p>

<h2>
	التمارين
</h2>

<h3>
	1. قاعدة بيانات: القسم الأول
</h3>

<p>
	عدّل تطبيق المكتبة بحيث يحفظ البيانات ضمن قاعدة بيانات. يمكن أن تجد تخطيط Mongoose للكتب ولمؤلفين على <a data-ss1621334060="1" href="https://github.com/fullstack-hy2020/misc/blob/master/library-schema.md" rel="external nofollow">Github</a>.
</p>

<p>
	لنغير تخطيط GraphQL للكتب قليلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_45" style="">
<span class="pln">type </span><span class="typ">Book</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  title</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
  published</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Int</span><span class="pun">!</span><span class="pln">
  author</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Author</span><span class="pun">!</span><span class="pln">
  genres</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">String</span><span class="pun">!]!</span><span class="pln">
  id</span><span class="pun">:</span><span class="pln"> ID</span><span class="pun">!</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لكي يحتوي كائن الكتاب على كل تفاصيل المؤلف وليس الاسم فقط.
</p>

<p>
	يمكنك أيضًا أن تفترض أن المستخدم لن يحاول إدخال كتب أو مؤلفين بشكل خاطئ، وبالتالي لن تهتم بمعالجة أخطاء التقييم.
</p>

<p>
	لا تهتم بالأمور التالية حاليًا:
</p>

<ul>
<li>
		الاستعلام <code>allBooks</code> مستخدمًا المعاملات.
	</li>
	<li>
		الحقل <code>bookCount</code> من الكائن author.
	</li>
	<li>
		الحقل <code>author</code> من الكتاب.
	</li>
	<li>
		الطفرة <code>editAuthor.</code>
	</li>
</ul>
<h3>
	2. قاعدة بيانات: القسم الثاني
</h3>

<p>
	أكمل تطبيقك بحيث تعمل كل الاستعلامات والطفرات بطريقة صحيحة، ماعدا <code>allBooks</code> مع المعامل <code>author</code>.
</p>

<p>
	يمكنك الاستعانة <a data-ss1621334060="1" href="https://docs.mongodb.com/manual/reference/operator/query/in" rel="external nofollow">بالمعلومات الواردة</a> في توثيق Mongoose.
</p>

<h3>
	3. قاعدة بيانات: القسم الثالث
</h3>

<p>
	أكمل التطبيق بحيث يصبح قادرًا على معالجة أخطاء التقييم (كتاب بعنوان قصير جدًا أو اسم مؤلف مثلًا) بطريقة منطقية. ويعني ذلك أن تسبب هذه الأخطاء الاستثناء <code>UserInputError</code> الذي يُرمى مع رسالة خطأ.
</p>

<h3>
	4. تسجيل دخول المستخدمين
</h3>

<p>
	أضف آلية لإدارة المستخدمين إلى التطبيق. وسّع التخطيط ليصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5679_49" style="">
<span class="pln">type </span><span class="typ">User</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  username</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
  favoriteGenre</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
  id</span><span class="pun">:</span><span class="pln"> ID</span><span class="pun">!</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

type </span><span class="typ">Token</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  value</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

type </span><span class="typ">Query</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ..</span><span class="pln">
  me</span><span class="pun">:</span><span class="pln"> </span><span class="typ">User</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

type </span><span class="typ">Mutation</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  createUser</span><span class="pun">(</span><span class="pln">
    username</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
    favoriteGenre</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
  </span><span class="pun">):</span><span class="pln"> </span><span class="typ">User</span><span class="pln">
  login</span><span class="pun">(</span><span class="pln">
    username</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
    password</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
  </span><span class="pun">):</span><span class="pln"> </span><span class="typ">Token</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أنشئ محللاتٍ للاستعلام <code>me</code> وللطفرتين الجديدتين <code>createUser</code> و<code>login</code>. يمكنك أيضًا اعتبار أنّ لكل المستخدمين نفس كلمة السر التي تُكتب مسبقًا ضمن الشيفرة.
</p>

<p>
	إجعل الطفرتين <code>addBook</code> و<code>editauthor</code> قابلتين للتنفيذ إذا أرفق الاستعلام بشهادة تحقق صالحة فقط.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a data-ss1621334060="1" href="(https://fullstackopen.com/en/part8/database_and_user_administration" rel="">DataBase and user administration</a> من سلسلة <a data-ss1621334060="1" href="https://fullstackopen.com/en/" rel="external nofollow">Deep Dive Into Modern Web Development</a>
</p>
]]></description><guid isPermaLink="false">1211</guid><pubDate>Mon, 03 May 2021 09:02:00 +0000</pubDate></item><item><title>&#x628;&#x646;&#x627;&#x621; &#x62A;&#x637;&#x628;&#x64A;&#x642; React &#x628;&#x627;&#x633;&#x62A;&#x639;&#x645;&#x627;&#x644; &#x62E;&#x627;&#x62F;&#x645; GraphQL</title><link>https://academy.hsoub.com/programming/javascript/react/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-%D8%A8%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-%D8%AE%D8%A7%D8%AF%D9%85-graphql-r1210/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_04/608165a8bc860_-React--GraphQL.png.9f3a042a82571a94def5701c98d1fbaf.png" /></p>

<p>
	سننجز في المرحلة القادمة تطبيق React باستخدام خادم GraphQL الذي <a data-ss1621333722="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D8%A9-graphql-server-td-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1208/" rel="">أنشأناه سابقًا</a>.
</p>

<p>
	يمكنك أن تجد شيفرة الخادم ضمن الفرع part8-3 في المستودع المخصص على <a data-ss1621333722="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-backend/tree/part8-3" rel="external nofollow">Github</a>.
</p>

<p>
	يمكن نظريًا استخدام GraphQL مع طلبات HTTP-POST. تظهر الصورة التالية مثالًا باستخدام Postman.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64361" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/graphql_http_post_with_postman_01.png.86969e68b55f943068ed1afc3bd53fec.png" rel=""><img alt="graphql_http_post_with_postman_01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64361" data-unique="w8yujzyv0" src="https://academy.hsoub.com/uploads/monthly_2021_04/graphql_http_post_with_postman_01.png.86969e68b55f943068ed1afc3bd53fec.png"></a>
</p>

<p>
	يعمل الاتصال بإرسال طلبات إلى <a data-ss1621333722="1" href="http://localhost:4000/graphql." rel="external nofollow">العنوان</a>، وسيكون الاستعلام نصًا مرسلًا كقيمة للمفتاح <code>query</code>. كما يمكن التعامل مع الاتصال بين React-app و GraphQL باستخدام Axios. لكن لا يبدو هذا الخيار منطقيًا في معظم الأحيان. فمن الأفضل استخدام مكتبة ذات إمكانيات أعلى قادرة على إزالة التفاصيل غير الضرورية من الاتصال. ستجد حاليًا خيارين جيدين، أولهما المكتبة <a data-ss1621333722="1" href="https://facebook.github.io/relay/" rel="external nofollow">Relay</a> من Facebook والمكتبة <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/" rel="external nofollow">Apollo Client</a>. وتُعَدّ Apollo الأكثر شعبية بينهما بلا منازع، لذلك سنستخدمها نحن أيضًا.
</p>

<h2>
	المكتبة Apollo client
</h2>

<p>
	سنستخدم في منهاجنا النسخة <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/v3.0-beta/" rel="external nofollow">3.0 بيتا التجريبية</a> من هذه المكتبة. وتعتبر النسخة 3 حتى هذا التاريخ (12.12.2020) هي آخر نسخة رسمية. لذلك تذكر عند قراءة التوثيق أن تختار توثيق النسخة 3:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64352" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/apollo_client_v3_beta_02.png.19da4d734502c12b2cd3d67efdf961ce.png" rel=""><img alt="apollo_client_v3_beta_02.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64352" data-unique="r3e9bpqmy" src="https://academy.hsoub.com/uploads/monthly_2021_04/apollo_client_v3_beta_02.png.19da4d734502c12b2cd3d67efdf961ce.png"></a>
</p>

<p>
	أنشئ تطبيق React-app جديد وثبّت الاعتماديات التي تتطلبها المكتبة <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/v3.0-beta/get-started/#installation" rel="external nofollow">Apollo client</a>. ويمكن القيام بذلك كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_76" style="">
<span class="pln">npm install </span><span class="lit">@apollo</span><span class="pun">/</span><span class="pln">client graphql</span></pre>

<p>
	سنبدأ تطبيقنا بكتابة الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_12" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">ReactDOM</span><span class="pln"> from </span><span class="str">'react-dom'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> from </span><span class="str">'./App'</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ApolloClient</span><span class="pun">,</span><span class="pln"> </span><span class="typ">HttpLink</span><span class="pun">,</span><span class="pln"> </span><span class="typ">InMemoryCache</span><span class="pun">,</span><span class="pln"> gql </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> client </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ApolloClient</span><span class="pun">({</span><span class="pln">
  cache</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">InMemoryCache</span><span class="pun">(),</span><span class="pln">
  link</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HttpLink</span><span class="pun">({</span><span class="pln">
    uri</span><span class="pun">:</span><span class="pln"> </span><span class="str">'http://localhost:4000'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> query </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
query </span><span class="pun">{</span><span class="pln">
  allPersons  </span><span class="pun">{</span><span class="pln">
    name</span><span class="pun">,</span><span class="pln">
    phone</span><span class="pun">,</span><span class="pln">
    address </span><span class="pun">{</span><span class="pln">
      street</span><span class="pun">,</span><span class="pln">
      city
    </span><span class="pun">}</span><span class="pln">
    id
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">`</span><span class="pln">

client</span><span class="pun">.</span><span class="pln">query</span><span class="pun">({</span><span class="pln"> query </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">

</span><span class="typ">ReactDOM</span><span class="pun">.</span><span class="pln">render</span><span class="pun">(&lt;</span><span class="typ">App</span><span class="pln"> </span><span class="pun">/&gt;,</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">'root'</span><span class="pun">))</span></pre>

<p>
	تنشئ الشيفرة في البداية كائن عميل <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/v3.0-beta/get-started/#create-a-client" rel="external nofollow">client</a>، ليُستخدم بعد ذلك لإرسال الاستعلام إلى الخادم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_14" style="">
<span class="pln">client</span><span class="pun">.</span><span class="pln">query</span><span class="pun">({</span><span class="pln"> query </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">})</span></pre>

<p>
	تُطبع استجابة الخادم على الطرفية كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64366" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/server_response_console_03.png.f5826ddb721544c3383e9c1577fc0703.png" rel=""><img alt="server_response_console_03.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64366" data-unique="nbfal6f13" src="https://academy.hsoub.com/uploads/monthly_2021_04/server_response_console_03.png.f5826ddb721544c3383e9c1577fc0703.png"></a>
</p>

<p>
	يمكن للتطبيق أن يتواصل مع خادم GraphQL بالاستفادة من الكائن <code>client</code>. يمكن أن نجعل هذا الكائن متاحًا لجميع مكوِّنات التطبيق بتغليف المكوِّن الرئيسي <code>App</code> ضمن المكوِّن <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/v3.0-beta/get-started/#connect-your-client-to-react" rel="external nofollow">ApolloProvider</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_17" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">ReactDOM</span><span class="pln"> from </span><span class="str">'react-dom'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> from </span><span class="str">'./App'</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> 
  </span><span class="typ">ApolloClient</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ApolloProvider</span><span class="pun">,</span><span class="pln"> </span><span class="typ">HttpLink</span><span class="pun">,</span><span class="pln"> </span><span class="typ">InMemoryCache</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pln"> 

</span><span class="kwd">const</span><span class="pln"> client </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ApolloClient</span><span class="pun">({</span><span class="pln">
  cache</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">InMemoryCache</span><span class="pun">(),</span><span class="pln">
  link</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HttpLink</span><span class="pun">({</span><span class="pln">
    uri</span><span class="pun">:</span><span class="pln"> </span><span class="str">'http://localhost:4000'</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

</span><span class="typ">ReactDOM</span><span class="pun">.</span><span class="pln">render</span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">ApolloProvider</span><span class="pln"> client</span><span class="pun">={</span><span class="pln">client</span><span class="pun">}&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">App</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="typ">ApolloProvider</span><span class="pun">&gt;,</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">'root'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">)</span></pre>

<h2>
	إنشاء الاستعلامات
</h2>

<p>
	نحن جاهزون الآن لإنجاز واجهة العرض الرئيسية للتطبيق، والتي تُظهر قائمة بأرقام الهواتف. تقدم المكتبة Apollo Client عدة بدائل لإنشاء <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/v3.0-beta/data/queries/" rel="external nofollow">الاستعلامات</a>. وتُعَدّ الممارسة الأفضل حاليًا هي استخدام دالة الخطاف <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/v3.0-beta/api/react/hooks/#usequery" rel="external nofollow">useQuery</a>.
</p>

<p>
	تُظهر الشيفرة التالية الاستعلام الذي ينشئه المكون <code>App</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_19" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> gql</span><span class="pun">,</span><span class="pln"> useQuery </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> ALL_PERSONS </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
query </span><span class="pun">{</span><span class="pln">
  allPersons  </span><span class="pun">{</span><span class="pln">
    name
    phone
    id
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">`</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> useQuery</span><span class="pun">(</span><span class="pln">ALL_PERSONS</span><span class="pun">)</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">loading</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">loading</span><span class="pun">...&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">allPersons</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">name</span><span class="pun">).</span><span class="pln">join</span><span class="pun">(</span><span class="str">', '</span><span class="pun">)}</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span></pre>

<p>
	ينفّذ الخطاف hook الذي يُدعى <code>useQuery</code>، عندما يُستدعى، الاستعلام الذي يُمرَّر إليه كمعامل، ويعيد كائنا مؤلفًا من عدة <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/v3.0-beta/api/react/hooks/#result" rel="external nofollow">حقول</a>. سيحمل الحقل <code>loading</code> القيمة true إن لم يتلقى الاستعلام استجابة بعد. ثم ستُصيَّر الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_21" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">loading </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">loading</span><span class="pun">...&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	عندما يتلقى الاستعلام <code>allPersons</code> الاستجابة، يمكن الحصول على النتيجة من الحقل <code>data</code>، كما يمكن تصيير قائمة الأسماء على الشاشة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_23" style="">
<span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">allPersons</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">name</span><span class="pun">).</span><span class="pln">join</span><span class="pun">(</span><span class="str">', '</span><span class="pun">)}</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span></pre>

<p>
	لنفصل الشيفرة التي تعرض قائمة الأشخاص ضمن مكوِّن خاص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_25" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">Persons</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> persons </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="typ">Persons</span><span class="pun">&lt;/</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">persons</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">div key</span><span class="pun">={</span><span class="pln">p</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}&gt;</span><span class="pln">
          </span><span class="pun">{</span><span class="pln">p</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln"> </span><span class="pun">{</span><span class="pln">p</span><span class="pun">.</span><span class="pln">phone</span><span class="pun">}</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">  
      </span><span class="pun">)}</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيبقى المكوّن <code>App</code> قادرًا على إنشاء الاستعلامات وتمرير النتائج إلى المكوّن الجديد لتُصيَّر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_29" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> useQuery</span><span class="pun">(</span><span class="pln">ALL_PERSONS</span><span class="pun">)</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">loading</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">loading</span><span class="pun">...&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">Persons</span><span class="pln"> persons </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">allPersons</span><span class="pun">}/&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	الاستعلامات والمتغيرات المسماة
</h2>

<p>
	لنضف وظيفة إظهار تفاصيل عنوان الشخص. سيفي الاستعلام <code>findPerson</code> بالغرض.
</p>

<p>
	لقد وضعنا قيمة جاهزة كمعامل في الاستعلام الذي أنشأناه في الفصل السابق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_31" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Arto Hellas"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    phone 
    city 
    street
    id
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لكن عندما ننجز الاستعلامات برمجيًا، لا بدّ من تمرير المعاملات ديناميكيًا. لهذا سنستخدم <a data-ss1621333722="1" href="https://graphql.org/learn/queries/#variables" rel="external nofollow">متغيرات</a> GraphQL التي ستفي بالغرض. ولكي نستخدم هذه المتغيرات، لا بدّ من تسمية الاستعلامات.
</p>

<p>
	تمثل الشيفرة التالية نموذجًا جيد لبناء استعلام:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_34" style="">
<span class="pln">query findPersonByName</span><span class="pun">(</span><span class="pln">$nameToSearch</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> $nameToSearch</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    name
    phone 
    address </span><span class="pun">{</span><span class="pln">
      street
      city
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُسمّى الاستعلام السابق <code>findPersonByName</code> ويقبل المتغير النصي <code>$nameToSearch</code> معاملًا. كما يمكن أن ننجز الاستعلامات مستخدمين أرضية عمل GraphQL. تُعطى المعاملات داخل متغيّرات الاستعلام:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64364" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/query_variables_04.png.964b3fe4494d2d4fddc43647b8c99400.png" rel=""><img alt="query_variables_04.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64364" data-unique="7yfd1qup5" src="https://academy.hsoub.com/uploads/monthly_2021_04/query_variables_04.png.964b3fe4494d2d4fddc43647b8c99400.png"></a>
</p>

<p>
	يلائم استخدام الخطاف <code>useQuery</code> الحالات التي يُنجز فيها الاستعلام عندما يُصيَّر المكوِّن. لكننا سننفذ الاستعلام الآن عندما يريد المستخدم أن يرى تفاصيل شخص معين فقط، أي سيُنفّذ الاستعلام <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/v3.0-beta/data/queries/#executing-queries-manually" rel="external nofollow">عند الحاجة</a>. في هذه الحالة ستكون دالة الخطاف <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/v3.0-beta/api/react/hooks/#uselazyquery" rel="external nofollow">useLazyQuery</a> خيارًا جيدًا.
</p>

<p>
	سيصبح المكوّن <code>Persons</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_36" style="">
<span class="kwd">const</span><span class="pln"> FIND_PERSON </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
query findPersonByName</span><span class="pun">(</span><span class="pln">$nameToSearch</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> $nameToSearch</span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
name
phone 
id
address
</span><span class="pun">{</span><span class="pln"> 
street 
city
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">}`</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Persons</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> persons </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">getPerson</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useLazyQuery</span><span class="pun">(</span><span class="pln">FIND_PERSON</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">person</span><span class="pun">,</span><span class="pln"> setPerson</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> showPerson </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      getPerson</span><span class="pun">({</span><span class="pln"> variables</span><span class="pun">:</span><span class="pln">
                 </span><span class="pun">{</span><span class="pln"> nameToSearch</span><span class="pun">:</span><span class="pln"> name </span><span class="pun">}</span><span class="pln">
                </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          setPerson</span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">findPerson</span><span class="pun">)</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">},</span><span class="pln"> </span><span class="pun">[</span><span class="pln">result</span><span class="pun">])</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">person</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pun">(</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">h2</span><span class="pun">&gt;{</span><span class="pln">person</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}&lt;/</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;{</span><span class="pln">person</span><span class="pun">.</span><span class="pln">address</span><span class="pun">.</span><span class="pln">street</span><span class="pun">}</span><span class="pln">
          </span><span class="pun">{</span><span class="pln">person</span><span class="pun">.</span><span class="pln">address</span><span class="pun">.</span><span class="pln">city</span><span class="pun">}&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;{</span><span class="pln">person</span><span class="pun">.</span><span class="pln">phone</span><span class="pun">}&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">button onClick</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setPerson</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">)}&gt;</span><span class="pln">close</span><span class="pun">&lt;</span><span class="str">/button&gt;      &lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
              </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">  
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="typ">Persons</span><span class="pun">&lt;/</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">persons</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">div key</span><span class="pun">={</span><span class="pln">p</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}&gt;</span><span class="pln">
          </span><span class="pun">{</span><span class="pln">p</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln"> </span><span class="pun">{</span><span class="pln">p</span><span class="pun">.</span><span class="pln">phone</span><span class="pun">}</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">button onClick</span><span class="pun">={()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> showPerson</span><span class="pun">(</span><span class="pln">p</span><span class="pun">.</span><span class="pln">name</span><span class="pun">)}</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln">
              show address
                  </span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">  
      </span><span class="pun">)}</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">Persons</span></pre>

<p>
	لقد تغيّرت الشيفرة، ولا تبدو معظم التغيّرات واضحة.
</p>

<p>
	فعندما ننقر على زر show address بجوار الشخص، سيُنفَّذ معالج الحدث <code>showPerson</code> الذي ينفِّذ بدوره استعلامًا لإحضار تفاصيل الأشخاص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_38" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">getPerson</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useLazyQuery</span><span class="pun">(</span><span class="pln">FIND_PERSON</span><span class="pun">)</span><span class="pln"> 

</span><span class="com">// ...</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> showPerson </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  getPerson</span><span class="pun">({</span><span class="pln"> variables</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> nameToSearch</span><span class="pun">:</span><span class="pln"> name </span><span class="pun">}</span><span class="pln"> </span><span class="pun">})</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يتلقى المتغيّر <code>nameToSearch</code> العائد للاستعلام قيمةً عندما يُنفَّذ هذا الاستعلام، وتُخزّن الاستجابة عليه في المتغيّر <code>result</code>، كما تُخزن قيمته في حالة المكوّن <code>person</code> في خطاف التأثير <code>useEffect</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_40" style="">
<span class="pln">useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setPerson</span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">findPerson</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">},</span><span class="pln"> </span><span class="pun">[</span><span class="pln">result</span><span class="pun">])</span></pre>

<p>
	يتلقى الخطاف المعامل الثاني <code>result</code>، وبالتالي ستُنفّذ الدالة التي تمرر إلى الخطاف كمعامل ثانٍ في كل مرة يحضر فيها الاستعلام تفاصيل شخص مختلف. ولو لم نتعامل مع التحديث بطريقة قابلة للإدارة باستخدام الخطاف كالسابق، ستسبب العودة من عرض شخص واحد إلى قائمة الأشخاص الكثير من المشاكل.
</p>

<p>
	إن احتوت الحالة <code>person</code> قيمة، فستظهر على الشاشة تفاصيل شخص واحد بدلًا من قائمة الأشخاص:
</p>

<p style="text-align: center;">
	<img alt="state_value_exists_problem_05.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64368" data-unique="jh5iqeiuo" src="https://academy.hsoub.com/uploads/monthly_2021_04/state_value_exists_problem_05.png.34f6f9528c24298a690363649656cea3.png"></p>

<p>
	عندما يريد المستخدم العودة إلى قائمة الأشخاص، يجب ضبط قيمة الحالة <code>person</code> على null.ولا يعتبر هذا الحل هو الأفضل، لكنه جيد بما يكفي في حالتنا.
</p>

<p>
	يمكنك إيجاد التطبيق بوضعه الحالي ضمن الفرع part8-1 في المستودع المخصص للتطبيق على <a data-ss1621333722="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-frontend/tree/part8-1" rel="external nofollow">Github</a>.
</p>

<h2>
	الذاكرة المؤقتة
</h2>

<p>
	إن نفذّنا عدة استعلامات مثل الاستعلام عن تفاصيل عنوان Arto Hella، فسنلاحظ أمرًا مهمًا: سيُرسل الاستعلام إلى الواجهة الخلفية مرة واحدة فقط. بعد ذلك، وعلى الرغم من تنفيذ الاستعلام مرات عدة من قبل الشيفرة، فلن يُرسل الاستعلام إلى الواجهة الخلفية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64365" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/same_qurey_sent_once_06.png.1872cb5fd071320a5cc326464132b41a.png" rel=""><img alt="same_qurey_sent_once_06.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64365" data-unique="hxz3h61ha" src="https://academy.hsoub.com/uploads/monthly_2021_04/same_qurey_sent_once_06.png.1872cb5fd071320a5cc326464132b41a.png"></a>
</p>

<p>
	تُخزَّن المكتبة الاستجابات على الاستعلامات في الذاكرة المؤقتة <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/v3.0-beta/caching/cache-configuration/" rel="external nofollow">cache</a>. ولاستمثال الأداء، لن ترسل المكتبة الاستعلام إن وجدت استجابة مسبقة عليه ضمن الذاكرة المؤقتة.
</p>

<p>
	يمكن تثبيت الأداة <a data-ss1621333722="1" href="https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm/related" rel="external nofollow">Apollo Client devtools</a> على المتصفح Chrome لمتابعة حالة الذاكرة المؤقتة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64357" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/cache_state_devtools_07.png.cc59aae3c5fdbd0b73bdbedd4310cd40.png" rel=""><img alt="cache_state_devtools_07.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64357" data-unique="wnv60wzbt" src="https://academy.hsoub.com/uploads/monthly_2021_04/cache_state_devtools_07.png.cc59aae3c5fdbd0b73bdbedd4310cd40.png"></a>
</p>

<p>
	تُنظّم البيانات داخل الذاكرة المؤقتة عن طريق الاستعلام. وطالما أن الكائن <code>Person</code> سيمتلك حقلًا يحتوي على معرّف فريد <code>id</code> من النوع "ID"، فإن أعيد نفس الكائن كاستجابة لعدة استعلامات، يمكن أن تدمجها المكتبة Apollo ضمن كائن واحد. وهكذا فإن تنفيذ الاستعلام <code>findPerson</code> للحصول على تفاصيل "Arto Hellas" سيٌحدّث تفاصيل العنوان حتى من أجل الاستعلام <code>allPersons</code>.
</p>

<h2>
	تنفيذ الطفرات
</h2>

<p>
	سنضيف الآن وظيفة إنشاء أشخاص جدد.
</p>

<p>
	لقد كتبنا في الفصل السابق المعاملات من أجل الطفرات mutations يدويًا. سنحتاج الآن نسخة من الطفرة <code>addPeson</code> تستخدم <a data-ss1621333722="1" href="https://graphql.org/learn/queries/#variables" rel="external nofollow">المتغيرات</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_42" style="">
<span class="kwd">const</span><span class="pln"> CREATE_PERSON </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
mutation createPerson</span><span class="pun">(</span><span class="pln">$name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!,</span><span class="pln"> $street</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!,</span><span class="pln"> $city</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!,</span><span class="pln"> $phone</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  addPerson</span><span class="pun">(</span><span class="pln">
    name</span><span class="pun">:</span><span class="pln"> $name</span><span class="pun">,</span><span class="pln">
    street</span><span class="pun">:</span><span class="pln"> $street</span><span class="pun">,</span><span class="pln">
    city</span><span class="pun">:</span><span class="pln"> $city</span><span class="pun">,</span><span class="pln">
    phone</span><span class="pun">:</span><span class="pln"> $phone
  </span><span class="pun">)</span><span class="pln"> 
</span><span class="pun">{</span><span class="pln">
    name
    phone
    id
    address </span><span class="pun">{</span><span class="pln">
      street
      city
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">`</span></pre>

<p>
	تؤمن دالة الخطاف <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/v3.0-beta/api/react/hooks/#usemutation" rel="external nofollow">useMutation</a> الآلية اللّازمة لإنشاء الطفرات. لننشئ الآن مكوّنًا جديدًا لإضافة شخص جديد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_44" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useState </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> gql</span><span class="pun">,</span><span class="pln"> useMutation </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> CREATE_PERSON </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">`</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">PersonForm</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> setName</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">phone</span><span class="pun">,</span><span class="pln"> setPhone</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">street</span><span class="pun">,</span><span class="pln"> setStreet</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">city</span><span class="pun">,</span><span class="pln"> setCity</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> createPerson </span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useMutation</span><span class="pun">(</span><span class="pln">CREATE_PERSON</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> submit </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    event</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">()</span><span class="pln">

    createPerson</span><span class="pun">({</span><span class="pln">
        variables</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> phone</span><span class="pun">,</span><span class="pln"> street</span><span class="pun">,</span><span class="pln"> city </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
    setName</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">
    setPhone</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">
    setStreet</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">
    setCity</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">create </span><span class="kwd">new</span><span class="pun">&lt;/</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">form onSubmit</span><span class="pun">={</span><span class="pln">submit</span><span class="pun">}&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          name </span><span class="pun">&lt;</span><span class="pln">input value</span><span class="pun">={</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
            onChange</span><span class="pun">={({</span><span class="pln"> target </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setName</span><span class="pun">(</span><span class="pln">target</span><span class="pun">.</span><span class="pln">value</span><span class="pun">)}</span><span class="pln">
          </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          phone </span><span class="pun">&lt;</span><span class="pln">input value</span><span class="pun">={</span><span class="pln">phone</span><span class="pun">}</span><span class="pln">
            onChange</span><span class="pun">={({</span><span class="pln"> target </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setPhone</span><span class="pun">(</span><span class="pln">target</span><span class="pun">.</span><span class="pln">value</span><span class="pun">)}</span><span class="pln">
          </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          street </span><span class="pun">&lt;</span><span class="pln">input value</span><span class="pun">={</span><span class="pln">street</span><span class="pun">}</span><span class="pln">
            onChange</span><span class="pun">={({</span><span class="pln"> target </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setStreet</span><span class="pun">(</span><span class="pln">target</span><span class="pun">.</span><span class="pln">value</span><span class="pun">)}</span><span class="pln">
          </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          city </span><span class="pun">&lt;</span><span class="pln">input value</span><span class="pun">={</span><span class="pln">city</span><span class="pun">}</span><span class="pln">
            onChange</span><span class="pun">={({</span><span class="pln"> target </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setCity</span><span class="pun">(</span><span class="pln">target</span><span class="pun">.</span><span class="pln">value</span><span class="pun">)}</span><span class="pln">
          </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">'submit'</span><span class="pun">&gt;</span><span class="pln">add</span><span class="pun">!&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">PersonForm</span></pre>

<p>
	إنّ شيفرة النموذج واضحة تمامًا، وقد علّمنا الأسطر التي تحوي نقاط هامة. يمكن تعريف دالة الطفرة باستخدام الخطاف <code>useMutation</code>. سيعيد الخطاف مصفوفة يحوي عنصرها الأول الدالة التي ستسبب الطفرة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_46" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> createPerson </span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useMutation</span><span class="pun">(</span><span class="pln">CREATE_PERSON</span><span class="pun">)</span></pre>

<p>
	سيتلقى متغير الاستعلام قيمًا عند تنفيذ الاستعلام:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_49" style="">
<span class="pln">createPerson</span><span class="pun">({</span><span class="pln">  variables</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> phone</span><span class="pun">,</span><span class="pln"> street</span><span class="pun">,</span><span class="pln"> city </span><span class="pun">}</span><span class="pln"> </span><span class="pun">})</span></pre>

<p>
	سيُضاف الشخص الجديد كما هو مطلوب، لكن الشاشة لن تُحدّث ما يعرض عليها. والسبب في ذلك أن Apollo client لن تحدّث تلقائيًا الذاكرة المؤقتة للتطبيق، وستبقى محتفظة بالحالة التي خزنتها الطفرة السابقة. يمكن تحديث الشاشة بإعادة تحميل الصفحة، حيث يتم تفريغ الذاكرة المؤقتة عندما نفعل ذلك. لكن لابدّ من وجود طريقة أفضل لتحديث الشاشة.
</p>

<h2>
	تحديث محتويات الذاكرة المؤقتة
</h2>

<p>
	هناك حلول عدة لإنجاز ذلك. تتمثل إحدى الحلول بحثِّ <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/v3.0-beta/data/queries/#polling" rel="external nofollow">poll</a> الخادم من قبل الاستعلام، أو تنفيذ الاستعلام بشكل دوري.
</p>

<p>
	سيكون التعديل في الشيفرة بسيطًا، إذ سنجعل الاستعلام يحثُّ الخادم كل ثانيتين:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_51" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> useQuery</span><span class="pun">(</span><span class="pln">ALL_PERSONS</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pollInterval</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2000</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">loading</span><span class="pun">)</span><span class="pln">  </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">loading</span><span class="pun">...&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">Persons</span><span class="pln"> persons </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">allPersons</span><span class="pun">}/&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">PersonForm</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span></pre>

<p>
	إن الحل المقدم بسيط، وسيظهر أي شخص جديد يضاف إلى القائمة على الشاشة التي تعرض كل الأشخاص مباشرة. لكن تتمثل الناحية السلبية لهذا الحل بانتقال بيانات بلا فائدة عبر الشبكة.
</p>

<p>
	الطريقة السهلة الأخرى لإبقاء الذاكرة المؤقتة متزامنة مع التحديثات التي تجري هي استخدام المعامل <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/v3.0-beta/api/react/hooks/#params-2" rel="external nofollow">refetchQueries</a> لدالة الخطاف <code>useMutation</code> والذي يحدد أنّ الاستعلام سيحضر تفاصيل كل الأشخاص من جديد في كل مرة يُضاف فيها شخص جديد.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_55" style="">
<span class="kwd">const</span><span class="pln"> ALL_PERSONS </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  query  </span><span class="pun">{</span><span class="pln">
    allPersons  </span><span class="pun">{</span><span class="pln">
      name
      phone
      id
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">`</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">PersonForm</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> createPerson </span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useMutation</span><span class="pun">(</span><span class="pln">CREATE_PERSON</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    refetchQueries</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> query</span><span class="pun">:</span><span class="pln"> ALL_PERSONS </span><span class="pun">}</span><span class="pln"> </span><span class="pun">]</span><span class="pln">  </span><span class="pun">})</span></pre>

<p>
	إن حسنات وسيئات هذا الحل هي على نقيض الحل السابق، إذ لا يوجد هنا انتقال بيانات زائد عبر الشبكة، فلا تُنفَّذ الاستعلامات بشكل دوري لالتقاط أي تغيير في حال حدوثه. لكن إن حدَّث مستخدم حالة الخادم، فلن تظهر التغييرات مباشرة لبقية المستخدمين.
</p>

<p>
	لا تزال هناك طرق أخرى لتحديث الذاكرة المؤقتة وسنستعرضها لاحقًا في هذا القسم.
</p>

<p>
	تتواجد حاليًا في تطبيقنا شيفرة الاستعلامات وشيفرة المكوّنات في نفس المكان، لنفصل إذًا شيفرة الاستعلامات ونضعها في الملف الجديد "queries.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_57" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> gql  </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> ALL_PERSONS </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  query </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">`</span><span class="pln">
</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> FIND_PERSON </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  query findPersonByName</span><span class="pun">(</span><span class="pln">$nameToSearch</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">`</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> CREATE_PERSON </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  mutation createPerson</span><span class="pun">(</span><span class="pln">$name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!,</span><span class="pln"> $street</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!,</span><span class="pln"> $city</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!,</span><span class="pln"> $phone</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">`</span></pre>

<p>
	يُدرج الآن كل مكوِّن الاستعلامات التي يحتاجها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_59" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> ALL_PERSONS </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'./queries'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> useQuery</span><span class="pun">(</span><span class="pln">ALL_PERSONS</span><span class="pun">)</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستجد الشيفرة الحالية للتطبيق ضمن الفرع part8-2 في المستودع المخصص للتطبيق على <a data-ss1621333722="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-frontend/tree/part8-2" rel="external nofollow">Github</a>.
</p>

<h2>
	التعامل مع أخطاء الطفرات
</h2>

<p>
	ستسبب محاولة إنشاء شخص جديد ببيانات غير صحيحة خطأً، وسينهار التطبيق ككل:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64353" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/app_fail_invalid_data_08.png.4fd825b7fbb0f934e2bce8baf3c4102b.png" rel=""><img alt="app_fail_invalid_data_08.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64353" data-unique="bfcdp9rw2" src="https://academy.hsoub.com/uploads/monthly_2021_04/app_fail_invalid_data_08.png.4fd825b7fbb0f934e2bce8baf3c4102b.png"></a>
</p>

<p>
	علينا اصطياد هذا الخطأ ومنع وقوعه. يمكن تعريف دالة معالج خطأ للطفرة باستخدام <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/v3.0-beta/api/react/hooks/#params-2" rel="external nofollow">الخيار</a> <code>onError</code> للخطاف <code>useMutaion</code>.
</p>

<p>
	لنعرّف معالج خطأ للطفرة يستخدم الدالة <code>setEroor</code> التي تُمرّر إليه كمعامل لتهيئة رسالة خطأ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_61" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">PersonForm</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> setError </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ... </span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> createPerson </span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useMutation</span><span class="pun">(</span><span class="pln">CREATE_PERSON</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    refetchQueries</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
        </span><span class="pun">{</span><span class="pln">query</span><span class="pun">:</span><span class="pln"> ALL_PERSONS </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">],</span><span class="pln">
    onError</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        setError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">.</span><span class="pln">graphQLErrors</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">message</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">

  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمكننا الآن تصيير رسالة الخطأ على الشاشة عند الحاجة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_63" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">errorMessage</span><span class="pun">,</span><span class="pln"> setErrorMessage</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> useQuery</span><span class="pun">(</span><span class="pln">ALL_PERSONS</span><span class="pun">)</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">loading</span><span class="pun">)</span><span class="pln">  </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">loading</span><span class="pun">...&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> notify </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      setErrorMessage</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln">
      setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          setErrorMessage</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">)</span><span class="pln">
      </span><span class="pun">},</span><span class="pln"> </span><span class="lit">10000</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">Notify</span><span class="pln"> errorMessage</span><span class="pun">={</span><span class="pln">errorMessage</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">Persons</span><span class="pln"> persons </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">allPersons</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="typ">PersonForm</span><span class="pln"> setError</span><span class="pun">={</span><span class="pln">notify</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">Notify</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln">errorMessage</span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="pun">!</span><span class="pln">errorMessage </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">null</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">div style</span><span class="pun">={{</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> </span><span class="str">'red'</span><span class="pun">}}&gt;</span><span class="pln"> 
        </span><span class="pun">{</span><span class="pln">errorMessage</span><span class="pun">}</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">  </span><span class="pun">)}</span></pre>

<p>
	يُبلّغ المستخدم الآن عن الخطأ المرتكب من خلال تنبيه بسيط.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64359" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/error_notification_09.png.a35b574fade392aea956ddd52ea48c25.png" rel=""><img alt="error_notification_09.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64359" data-unique="s08nzbife" src="https://academy.hsoub.com/uploads/monthly_2021_04/error_notification_09.png.a35b574fade392aea956ddd52ea48c25.png"></a>
</p>

<p>
	ستجد الشيفرة الحالية للتطبيق ضمن الفرع part8-3 في المستودع المخصص للتطبيق على <a data-ss1621333722="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-frontend/tree/part8-3" rel="external nofollow">Github</a>.
</p>

<h2>
	تحديث رقم الهاتف
</h2>

<p>
	لنضف إمكانية تغيير رقم هاتف الشخص إلى التطبيق. سيكون الحل مطابقًا تقريبًا لعملية إضافة شخص جديد.
</p>

<p>
	تحتاج الطفرة مرة أخرى إلى معاملات.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_65" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> EDIT_NUMBER </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  mutation editNumber</span><span class="pun">(</span><span class="pln">$name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!,</span><span class="pln"> $phone</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    editNumber</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> $name</span><span class="pun">,</span><span class="pln"> phone</span><span class="pun">:</span><span class="pln"> $phone</span><span class="pun">)</span><span class="pln">  </span><span class="pun">{</span><span class="pln">
      name
      phone
      address </span><span class="pun">{</span><span class="pln">
        street
        city
      </span><span class="pun">}</span><span class="pln">
      id
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">`</span></pre>

<p>
	إن آلية عمل المكوّن <code>PhoneForm</code> المسؤول عن التغيير مباشرة. إذ يمتلك النموذج حقولًا لاسم الشخص ورقم الهاتف الجديد ويستدعي بعدها الدالة <code>changeNumber</code>. تُنفَّذ الدالة باستخدام الخطاف <code>useMutaion</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_67" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useState </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'react'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> useMutation </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> EDIT_NUMBER </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'../queries'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">PhoneForm</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> setName</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">phone</span><span class="pun">,</span><span class="pln"> setPhone</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> changeNumber </span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useMutation</span><span class="pun">(</span><span class="pln">EDIT_NUMBER</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> submit </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    event</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">()</span><span class="pln">

    changeNumber</span><span class="pun">({</span><span class="pln"> variables</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> phone </span><span class="pun">}</span><span class="pln"> </span><span class="pun">})</span><span class="pln">
    setName</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">
    setPhone</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">change number</span><span class="pun">&lt;/</span><span class="pln">h2</span><span class="pun">&gt;</span><span class="pln">

      </span><span class="pun">&lt;</span><span class="pln">form onSubmit</span><span class="pun">={</span><span class="pln">submit</span><span class="pun">}&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          name </span><span class="pun">&lt;</span><span class="pln">input
            value</span><span class="pun">={</span><span class="pln">name</span><span class="pun">}</span><span class="pln">
            onChange</span><span class="pun">={({</span><span class="pln"> target </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setName</span><span class="pun">(</span><span class="pln">target</span><span class="pun">.</span><span class="pln">value</span><span class="pun">)}</span><span class="pln">
          </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          phone </span><span class="pun">&lt;</span><span class="pln">input
            value</span><span class="pun">={</span><span class="pln">phone</span><span class="pun">}</span><span class="pln">
            onChange</span><span class="pun">={({</span><span class="pln"> target </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> setPhone</span><span class="pun">(</span><span class="pln">target</span><span class="pun">.</span><span class="pln">value</span><span class="pun">)}</span><span class="pln">
          </span><span class="pun">/&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button type</span><span class="pun">=</span><span class="str">'submit'</span><span class="pun">&gt;</span><span class="pln">change number</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">PhoneForm</span></pre>

<p>
	لا يبدو المظهر مرضيًا لكن التطبيق يعمل:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64363" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/person_number_change_10.png.7aacb81fe3e98b8adb125e110b92bbd1.png" rel=""><img alt="person_number_change_10.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64363" data-unique="eolbgpsir" src="https://academy.hsoub.com/uploads/monthly_2021_04/person_number_change_10.png.7aacb81fe3e98b8adb125e110b92bbd1.png"></a>
</p>

<p>
	وبشكل مفاجئ سيظهر الرقم الجديد للشخص تلقائيًا عند تغييره ضمن قائمة الأشخاص التي يصيرها المكوّن <code>Persons</code>. ويحدث هذا لأن كل شخص يمتلك حقلًا مُعرِّفًا فريدًا من النوع ID. وهكذا سوف تُحدَّث بيانات الشخص المخزّنة في الذاكرة المؤقتة تلقائيًا عندما تغيّرها الطفرة.
</p>

<p>
	ستجد الشيفرة الحالية للتطبيق ضمن الفرع part8-4 في المستودع المخصص للتطبيق على <a data-ss1621333722="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-frontend/tree/part8-4" rel="external nofollow">Github</a>.
</p>

<p>
	تبقى هناك ثغرة صغيرة في التطبيق. فلو أردنا تغيير رقم الهاتف لشخص غير موجود، يبدو أن لا شيء سيحدث. وذلك لأن شخصًا باسم غير موجود سيجعل الطفرة تعيد القيمة "null".
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64362" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/mutation_responded_null_11.png.b63ef8f2e100c3e04fb5fd26554d1af0.png" rel=""><img alt="mutation_responded_null_11.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64362" data-unique="jj5f6uv3h" src="https://academy.hsoub.com/uploads/monthly_2021_04/mutation_responded_null_11.png.b63ef8f2e100c3e04fb5fd26554d1af0.png"></a>
</p>

<p>
	لا يعتبر هذا الأمر خطأً بالنسبة للمكتبة GraphQL، وبالتالي لا فائدة من تعريف معالج خطأ في هذه الحالة.
</p>

<p>
	يمكن الاستفادة من الحقل <code>result</code> الذي يعيده الخطاف كمعامل ثانٍ لتوليد رسالة خطأ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_69" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">PhoneForm</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> setError </span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> setName</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">phone</span><span class="pun">,</span><span class="pln"> setPhone</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="str">''</span><span class="pun">)</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln"> changeNumber</span><span class="pun">,</span><span class="pln"> result </span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useMutation</span><span class="pun">(</span><span class="pln">EDIT_NUMBER</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> submit </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data </span><span class="pun">&amp;&amp;</span><span class="pln"> result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">editNumber </span><span class="pun">===</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">)</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">
          setError</span><span class="pun">(</span><span class="str">'person not found'</span><span class="pun">)</span><span class="pln">                                         
      </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">},</span><span class="pln"> </span><span class="pun">[</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">])</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إن لم يستطع التطبيق إيجاد شخص أو كانت نتيجة الأمر <code>result.data.editNumber</code> هيnull، سيستخدم المكوِّن دالة الاستدعاء التي يتلقاها كخاصية لإنشاء رسالة خطأ مناسبة. نريد في التطبيق أن نضبط رسالة الخطأ عندما تتغير نتيجة الطفرة <code>result.data</code> فقط. لذلك نستخدم خطاف التأثير <code>useEffect</code> للتحكم بإنشاء رسالة الخطأ.
</p>

<p>
	سيسبب استخدام الخطاف <code>useEffect</code> تحذيرًا يطلقه المدقق ESlint:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64360" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/eslint_useeffect_warning_12.png.de4e6843315636b4807e4dd20c5157f1.png" rel=""><img alt="eslint_useeffect_warning_12.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64360" data-unique="k8o3lqk48" src="https://academy.hsoub.com/uploads/monthly_2021_04/eslint_useeffect_warning_12.png.de4e6843315636b4807e4dd20c5157f1.png"></a>
</p>

<p>
	لا فائدة حقيقية من هذا التحذير و سيكون الحل الأفضل هو تجاهل قاعدة المدقق ESlint في السطر الذي سيولد التحذير:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_71" style="">
<span class="pln">useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">!</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">editNumber</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setError</span><span class="pun">(</span><span class="str">'name not found'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">},</span><span class="pln"> </span><span class="pun">[</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">])</span><span class="pln">  </span><span class="com">// eslint-disable-line </span></pre>

<p>
	يمكننا أيضًا التخلص من هذا التحذير بإضافة الدالة <code>setError</code> إلى مصفوفة المعامل الثاني للخطاف <code>useEffect</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4549_73" style="">
<span class="pln">useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">!</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">editNumber</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setError</span><span class="pun">(</span><span class="str">'name not found'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">},</span><span class="pln"> </span><span class="pun">[</span><span class="pln">result</span><span class="pun">.</span><span class="pln">data</span><span class="pun">,</span><span class="pln"> setError</span><span class="pun">])</span></pre>

<p>
	لن يعمل هذا الحل بالطبع إن كانت الدالة <code>notify</code> غير مضمنة داخل دالة الخطاف <a data-ss1621333722="1" href="https://reactjs.org/docs/hooks-reference.html#usecallback" rel="external nofollow">useCallback</a>. وإن لم تكن كذلك ستكون النتيجة حلقة فارغة لانهائية. عندما يُصيَّر المكوِن <code>App</code> بعد إزالة التنبيه، سينشأ تنبيه جديد مسببًا إعادة تنفيذ دالة خطاف التأثير، والتي ستولد بدورها تنبيهًا جديدًا وهكذا تستمر الحلقة.
</p>

<p>
	ستجد الشيفرة الحالية للتطبيق ضمن الفرع part8-5 في المستودع المخصص للتطبيق على <a data-ss1621333722="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-frontend/tree/part8-5" rel="external nofollow">Github</a>.
</p>

<h2>
	حالة التطبيق عند استخدام المكتبة Apollo client
</h2>

<p>
	لاحظنا في التطبيق السابق، أنّ إدارة الحالة كانت مسؤولية المكتبة Apollo client. وهذا حل نموذجي لتطبيقات GraphQL. وقد استخدم تطبيقنا حالة مكوِّنات React لإدارة حالة النماذج فقط ولإظهار تنبيهات الأخطاء. فعند استخدامك للمكتبة GraphQL، لا توجد مبررات مقبولة لنقل إدارة حالة التطبيقات إلى Redux إطلاقًا.
</p>

<p>
	وعند الحاجة ستخزِّن المكتبة Apollo حالة التطبيق المحليّة في <a data-ss1621333722="1" href="https://www.apollographql.com/docs/react/local-state/local-state-management" rel="external nofollow">ذاكرتها المؤقتة</a>.
</p>

<h2>
	التمرينات
</h2>

<p>
	سننجز خلال التمرينات القادمة واجهة أمامية للمكتبة GraphQl. استخدم <a data-ss1621333722="1" href="https://github.com/fullstack-hy2020/library-frontend" rel="external nofollow">المشروع المتعلق بالتمارين</a> كنقطة انطلاق لتطبيقك.
</p>

<p>
	يمكنك إنجاز المطلوب باستخدام المكونين <code>Query</code> و<code>Mutation</code> القابلين للتصيير في المكتبة Apollo client، كما يمكن استخدام الخطافات التي تؤمنها النسخة Beta 3.0.
</p>

<h3>
	1. واجهة عرض للمؤلفين
</h3>

<p>
	نفّذ واجهة تعرض تفاصيل جميع المؤلفين على الصفحة كما في الشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64354" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/authors_view_13.png.4bded6d925c373dfcd04e6b893c13859.png" rel=""><img alt="authors_view_13.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64354" data-unique="debkdtwn9" src="https://academy.hsoub.com/uploads/monthly_2021_04/authors_view_13.png.4bded6d925c373dfcd04e6b893c13859.png"></a>
</p>

<h3>
	2. واجهة عرض للكتب
</h3>

<p>
	نفِّذ واجهة تعرض على الصفحة كل التفاصيل الأخرى للكتب ما عدا نوعها.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64356" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/books-view_14.png.9efc3b009c243ca5cdee3bd223cf9e0e.png" rel=""><img alt="books-view_14.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64356" data-unique="ryw64ai61" src="https://academy.hsoub.com/uploads/monthly_2021_04/books-view_14.png.9efc3b009c243ca5cdee3bd223cf9e0e.png"></a>
</p>

<h3>
	3. إضافة كتاب
</h3>

<p>
	أنجز آلية لإضافة كتب جديدة إلى تطبيقك. يمكن أن تبدو هذه الوظيفة بالشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64351" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/6081659fd068d_add_new_book_15.png.74efe50db7cca56f6746275996818f9a.png" rel=""><img alt="add_new _book_15.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64351" data-unique="pfz5x05d9" src="https://academy.hsoub.com/uploads/monthly_2021_04/6081659fd068d_add_new_book_15.png.74efe50db7cca56f6746275996818f9a.png"></a>
</p>

<p>
	تأكد من أنّ واجهتي عرض المؤلفين والكتب ستبقيان مُحدَّثتين بعد إضافة كتاب جديد.
</p>

<p>
	تحقق عبر طرفية التطوير من استجابة الخادم في حال واجهتك المشاكل عند استخدام الاستعلامات أو الطفرات.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64358" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/developers_console_server_response_check_16.png.2b02221f22e043477987bcf2d72aaf99.png" rel=""><img alt="developers_console_server_response_check_16.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64358" data-unique="5khk2d971" src="https://academy.hsoub.com/uploads/monthly_2021_04/developers_console_server_response_check_16.png.2b02221f22e043477987bcf2d72aaf99.png"></a>
</p>

<h3>
	4. عام ولادة المؤلف
</h3>

<p>
	أنجز آلية لإضافة عام ولادة المؤلف. يمكنك إنشاء واجهة عرض جديدة لضبط عام الميلاد، أو يمكنك عرضها ضمن واجهة عرض المؤلفين:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64367" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/set_authors_birth_year_17.png.903e0ce134bab03c47056afbd6cee619.png" rel=""><img alt="set_authors_birth_year_17.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64367" data-unique="m2b1bd51y" src="https://academy.hsoub.com/uploads/monthly_2021_04/set_authors_birth_year_17.png.903e0ce134bab03c47056afbd6cee619.png"></a>
</p>

<p>
	تأكد من تحديث واجهة عرض المؤلفين بعد إضافة عام ولادة مؤلف.
</p>

<h3>
	5. أسلوب متقدم لإضافة عام ولادة مؤلف
</h3>

<p>
	عدّل نموذج إضافة عام الميلاد بحيث لا يمكن إضافته إلا لمؤلف موجود مسبقًا. استخدم المكتبتين <a data-ss1621333722="1" href="https://reactjs.org/docs/forms.html#the-select-tag" rel="external nofollow">select-tag</a>، و<a data-ss1621333722="1" href="https://github.com/JedWatson/react-select" rel="external nofollow">react-select</a> أو أية آليات مناسبة أخرى.
</p>

<p>
	سيبدو الحل الذي يستخدم React-select مشابهًا للشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64355" data-ss1621333722="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/birthyear_using_react_select_18.png.a4e4888c36af0a9bb9664507998d9225.png" rel=""><img alt="birthyear_using_react_select_18.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64355" data-unique="1mpt162m6" src="https://academy.hsoub.com/uploads/monthly_2021_04/birthyear_using_react_select_18.png.a4e4888c36af0a9bb9664507998d9225.png"></a>
</p>

<p>
	ترجمة -وبتصرف- للفصل <a data-ss1621333722="1" href="https://fullstackopen.com/en/part8/react_and_graph_ql" rel="external nofollow">React and GraphQL</a> من سلسلة <a data-ss1621333722="1" href="https://fullstackopen.com/en/" rel="external nofollow">Deep Dive Into Modern Web Development</a>
</p>
]]></description><guid isPermaLink="false">1210</guid><pubDate>Fri, 30 Apr 2021 09:01:00 +0000</pubDate></item><item><title>&#x62A;&#x645;&#x627;&#x631;&#x64A;&#x646; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x64A;&#x629;: &#x62A;&#x648;&#x633;&#x64A;&#x639; &#x642;&#x627;&#x626;&#x645;&#x629; &#x627;&#x644;&#x645;&#x62F;&#x648;&#x646;&#x627;&#x62A; &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642; &#x645;&#x62F;&#x648;&#x646;&#x629; &#x645;&#x628;&#x646;&#x64A; &#x641;&#x64A; React</title><link>https://academy.hsoub.com/programming/javascript/react/%D8%AA%D9%85%D8%A7%D8%B1%D9%8A%D9%86-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D9%8A%D8%A9-%D8%AA%D9%88%D8%B3%D9%8A%D8%B9-%D9%82%D8%A7%D8%A6%D9%85%D8%A9-%D8%A7%D9%84%D9%85%D8%AF%D9%88%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%85%D8%AF%D9%88%D9%86%D8%A9-%D9%85%D8%A8%D9%86%D9%8A-%D9%81%D9%8A-react-r1171/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/604e5a44c15df_----------React.png.626c0359a80ff9d95acf687af95540ac.png" /></p>

<p>
	بالإضافة إلى التمارين الثمانية في الفصلين (المكتبة React-Router) و(خطافات مخصصة) من القسم السابع، هنالك 13 تمرينًا نتابع فيها العمل مع تطبيق قائمة المدونات الذي بدأناه في <a data-ss1615747647="1" href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%87%D9%8A%D9%83%D9%84-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%AE%D9%84%D9%81%D9%8A%D8%A9-%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-unit-tests-r1132/" rel="">القسم 4</a> و<a data-ss1615747647="1" href="https://academy.hsoub.com/programming/javascript/react/%D8%AA%D8%B3%D8%AC%D9%8A%D9%84-%D8%A7%D9%84%D8%AF%D8%AE%D9%88%D9%84-%D9%81%D9%8A-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A3%D9%85%D8%A7%D9%85%D9%8A%D8%A9-r1136/" rel="">القسم 5</a> من مادة هذا المنهاج. بعض التمارين التي سنراها هي "ميزات" مستقلة عن بعضها، بمعنى أنه لا يجب عليك إنجاز الحلول بترتيب معين. إذ يمكنك أن تتجاوز جزء من التمارين إن أردت ذلك.
</p>

<p>
	إن لم تستخدم تطبيق قائمة المدونات الخاص بك، يمكنك العمل على شيفرة نموذج الحل كنقطة انطلاق.
</p>

<p>
	ستتطلب منك العديد من التمرينات في هذا القسم أن تعيد كتابة الشيفرة الموجودة. وهذا الأمر طبيعي عند توسيع أي تطبيق، فإعادة كتابة الشيفرة هو أمر مهم ومهارة ضرورية حتى لو بدا الأمر صعبًا ومزعجًا في بعض الأحيان. وكنصيحة مفيدة بخصوص كتابة شيفرة جديدة أو إعادة كتابة شيفرة موجودة، هي أن تخطو خطوات صغيرة، فستفقد أعصابك بكل تأكيد إن تركت تطبيق ينهار بالكامل لأوقات طويلة أثناء إعادة كتابة الشيفرة.
</p>

<h2>
	التمارين 7.9 - 7.21
</h2>

<h3>
	7.9 المكتبة Redux: الخطوة 1
</h3>

<p>
	أعد كتابة تطبيقك لكي يستخدم Redux لإدارة حالة التطبيق بدلًا من استخدام الحالة الداخلية لمكونات React. وعدّل في آلية عرض التنبيهات لتستخدم Redux في هذه المرحلة من مجموعة التمرينات.
</p>

<h3>
	7.10 المكتبة Redux: الخطوة 2
</h3>

<p>
	انتبه إنّ هذا التمرين والتمرينان التاليان له، يتطلبان جهدًا، لكنهما يقدمان فائدة تعليمية كبيرة.
</p>

<p>
	خزن المعلومات حول منشورات المدونة في مخزن Redux. يكفي أن ترى المدونات ضمن الواجهة الخلفية، وأن تكون قادرًا على إنشاء مدونة جديدة في هذه المرحلة.
</p>

<p>
	لك كامل الحرية في أن تدير حالة تسجيل الدخول وإضافة مدونة جديدة باستخدام Redux أو حالة المكونات الداخلية.
</p>

<h3>
	7.11 المكتبة Redux: الخطوة 3
</h3>

<p>
	وسع التطبيق لتكون قادرُا من جديد على الإعجاب بالمدونات أو حذفها.
</p>

<h3>
	7.12 المكتبة Redux: الخطوة 4
</h3>

<p>
	خزّن معلومات تسجيل دخول المستخدم ضمن مخزن Redux.
</p>

<h3>
	7.13 واجهة عرض المستخدمين
</h3>

<p>
	أضف واجهة عرض للتطبيق لكي يعرض جميع المعلومات الأساسية المتعلقة بالمستخدم.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1615747647="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.13_01.png.e28794e4886a89d7217ad289a3c157ae.png" data-fileid="59680" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="59680" data-unique="lcwh0kcll" src="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.13_01.png.e28794e4886a89d7217ad289a3c157ae.png" alt="exe_7.13_01.png"></a>
</p>

<h3>
	7.14 واجهة عرض لمستخدم واحد
</h3>

<p>
	أضف واجهة عرض للتطبيق لكي يعرض كل المنشورات المتعلقة بمستخدم واحد.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1615747647="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.14_02.png.0906362b315657de2f19e5b24f394f8b.png" data-fileid="59681" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="59681" data-unique="orvzyovgq" src="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.14_02.png.0906362b315657de2f19e5b24f394f8b.png" alt="exe_7.14_02.png"></a>
</p>

<p>
	يمكنك الوصول إلى هذه الواجهة بالنقر على اسم المستخدم من واجهة عرض كل المستخدمين:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1615747647="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.14_03.png.7e62a45ac1a74ab4ac90cf710eaca4f7.png" data-fileid="59682" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="59682" data-unique="wl9dc5y1f" src="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.14_03.png.7e62a45ac1a74ab4ac90cf710eaca4f7.png" alt="exe_7.14_03.png"></a>
</p>

<p>
	<strong>ملاحظة</strong>: ستتعثر حتمًا برسالة الخطأ التالية خلال حلك للتمرين:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1615747647="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.14_error_message_04.png.743442c0b19b73a6cf00cbc8f03e7d3d.png" data-fileid="59683" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="59683" data-unique="deftsw1yp" src="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.14_error_message_04.png.743442c0b19b73a6cf00cbc8f03e7d3d.png" alt="exe_7.14_error_message_04.png"></a>
</p>

<p>
	سيحدث هذا الخطأ عندما تُحدِّث صفحة المتصفح وهي تعرض واجهة مستخدم واحد. إنّ سبب هذه المشكلة يتعلق بالانتقال المباشر إلى صفحة المستخدم، فلم تتلقَّ React بعد البيانات من الواجهة الخلفية. ويعتبر التصيير الشرطي أحد الحلول المقترحة للمشكلة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5821_11" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">User</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">...</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">user</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">    
      </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">null</span><span class="pln">  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="com">// ...</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	7.15 واجهة عرض المدوّنة
</h3>

<p>
	أضف واجهة عرض خاصة بمنشورات المدونة. يمكنك تخطيط طريق العرض اعتمادًا على النموذج التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1615747647="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.15_blog_view_05.png.7b7b84f570b2c45a0c92b29b35a2c089.png" data-fileid="59684" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="59684" data-unique="8sbvw6wz9" src="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.15_blog_view_05.png.7b7b84f570b2c45a0c92b29b35a2c089.png" alt="exe_7.15_blog_view_05.png"></a>
</p>

<p>
	يجب أن يكون المستخدم قادرًا على الوصول إلى المنشور بالنقر على اسمه في الواجهة التي تعرض كل المنشورات:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1615747647="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.15_blog_view_06.png.bf79faae63d2449cb25517a24dadd78e.png" data-fileid="59685" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="59685" data-unique="u3ieqq8b6" src="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.15_blog_view_06.png.bf79faae63d2449cb25517a24dadd78e.png" alt="exe_7.15_blog_view_06.png"></a>
</p>

<p>
	بعد إكمالك لهذا التمرين، يمكنك الاستغناء عن الوظيفة التي أضفتها إلى التطبيق في التمرين 5.6. فلا داعي لتوسيع العنصر عند النقر على المنشور لعرض تفاصيله.
</p>

<h3>
	7.16 أدوات التنقل
</h3>

<p>
	أضف قائمة للتنقل في واجهات العرض في التطبيق
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1615747647="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.16_nav_07.png.0e0a36eb3d29be2bc2a4b58e90986a1f.png" data-fileid="59686" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="59686" data-unique="ykczjgp6x" src="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.16_nav_07.png.0e0a36eb3d29be2bc2a4b58e90986a1f.png" alt="exe_7.16_nav_07.png"></a>
</p>

<h3>
	7.17 التعليقات: الخطوة 1
</h3>

<p>
	أضف وظيفة تجعل المستخدم قادرًا على إدراج تعليق حول المنشور:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1615747647="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.17_comments_08.png.c26a1939155bcc54489696734c2d5657.png" data-fileid="59687" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="59687" data-unique="ylrvr1cqd" src="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.17_comments_08.png.c26a1939155bcc54489696734c2d5657.png" alt="exe_7.17_comments_08.png"></a>
</p>

<p>
	يجب أن يكون التعليق بلا هوية، أي يجب أن لا يقترن التعليق بالمستخدم الذي كتبه.
</p>

<p>
	يكفي في هذه المرحلة أن تعرض الواجهة الأمامية التعليقات التي يتلقاها التطبيق من الواجهة الخلفية.
</p>

<p>
	إحدى الطرق المناسبة لإضافة التعليقات على المنشورات بإرسال طلب HTTP-POST إلى عنوان الموقع "api/blogs/:id/comments".
</p>

<h3>
	7.18 التعليقات: الخطوة 2
</h3>

<p>
	وسّع التطبيق لكي يصبح المستخدم قادرًا على التعليق على منشور من الواجهة الأمامية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1615747647="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.18_comments_09.png.bfc2fd5673074f8fbc7ac59a1d6d4d1d.png" data-fileid="59688" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="59688" data-unique="i2hfxn5rx" src="https://academy.hsoub.com/uploads/monthly_2021_03/exe_7.18_comments_09.png.bfc2fd5673074f8fbc7ac59a1d6d4d1d.png" alt="exe_7.18_comments_09.png"></a>
</p>

<h3>
	7.19 التنسيقات: الخطوة 1
</h3>

<p>
	حسّن من مظهر التطبيق مستخدمُا إحدى الطرق التي تعلمناها في مادة المنهاج.
</p>

<h3>
	7.20 التنسيقات: الخطوة 2
</h3>

<p>
	يمكنك أن تشير إلى إنجازك لهذا التمرين، إن استغرقت ساعة أو أكثر في تنسيق التطبيق.
</p>

<h3>
	7.21 رأيك في المنهاج
</h3>

<p>
	كيف أبليت حتى الآن؟ أعطنا رأيك بالمنهاج على منصة Moodle.
</p>

<p>
	هكذا نكون وصلنا إلى آخر تمرينات هذا القسم، وقد حان الوقت لتسليم إجاباتك إلى GitHub. لا تنس الإشارة إلى كل التمارين التي أنجزتها في <a data-ss1615747647="1" href="https://studies.cs.helsinki.fi/stats/courses/fullstackopen" rel="external nofollow">منظومة تسليم التمارين</a>.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a data-ss1615747647="1" href="https://fullstackopen.com/en/part7/exercises_extending_the_bloglist" rel="external nofollow">exercises extending the bloglist</a> من سلسلة <a data-ss1615747647="1" href="https://fullstackopen.com/en/" rel="external nofollow">Deep Dive Into Modern Web Development</a>
</p>
]]></description><guid isPermaLink="false">1171</guid><pubDate>Sun, 14 Mar 2021 18:50:34 +0000</pubDate></item><item><title>&#x645;&#x643;&#x648;&#x646;&#x627;&#x62A; &#x627;&#x644;&#x623;&#x635;&#x646;&#x627;&#x641; &#x648;&#x645;&#x648;&#x627;&#x636;&#x64A;&#x639; &#x623;&#x62E;&#x631;&#x649; &#x645;&#x647;&#x645;&#x629; &#x641;&#x64A; &#x628;&#x646;&#x627;&#x621; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; React</title><link>https://academy.hsoub.com/programming/javascript/react/%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%88%D9%85%D9%88%D8%A7%D8%B6%D9%8A%D8%B9-%D8%A3%D8%AE%D8%B1%D9%89-%D9%85%D9%87%D9%85%D8%A9-%D9%81%D9%8A-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-react-r1170/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/604e57cc38a4e_--------React.png.5c7feec18eda6b908d17a3400505ff58.png" /></p>

<h2>
	مكونات الأصناف
</h2>

<p>
	استخدمنا حتى اللحظة في المنهاج مكوّنات React التي عرفناها على شكل دوال JavaScript. ولم يكن هذا ممكنًا لولا الوظائف التي أمنتها <a data-ss1615747012="1" href="https://wiki.hsoub.com/React/hooks_intro" rel="external">الخطافات</a> التي أتت مع النسخة 16.8 من React. فلقد كان على المطوّر أن يستخدم العبارة <a data-ss1615747012="1" href="https://wiki.hsoub.com/React/state_and_lifecycle#.D8.AA.D8.AD.D9.88.D9.8A.D9.84_.D8.A7.D9.84.D8.AF.D8.A7.D9.84.D8.A9_.D8.A5.D9.84.D9.89_.D8.B5.D9.86.D9.81" rel="external">Class</a> عندما يعرّف مكونًا له حالة.
</p>

<p>
	من المفيد أن نطّلع إلى حدٍّ ما على مكوّنات الأصناف، لأنّ هناك الكثير من تطبيقات React القديمة التي لم تُكتب من جديد باستخدام القواعد الأحدث.
</p>

<p>
	لنتعرّف إذًا على الميزات الرئيسية لمكوّنات الأصناف، وذلك باستخدامها مع تطبيق " الطرائف" الذي أصبح مألوفًا. سنخزّن الطرائف في الملف "db.json" مستخدمين خادم JSON. يمكن نسخ محتويات الملف من <a data-ss1615747012="1" href="https://github.com/fullstack-hy2020/misc/blob/master/anecdotes.json" rel="external nofollow">GitHub</a>
</p>

<p>
	تبدو النسخة الأساسية من مكوّن الأصناف كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2161_7" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="typ">React</span><span class="pln"> from </span><span class="str">'react'</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> extends </span><span class="typ">React</span><span class="pun">.</span><span class="typ">Component</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  render</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">anecdote of the day</span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="typ">App</span></pre>

<p>
	يمتلك المكوّن الآن <a data-ss1615747012="1" href="https://wiki.hsoub.com/React/react_component#constructor.28.29" rel="external">دالة بانية</a>، والتي لا تقوم بأي عمل حاليًا، كما يتضمن أيضًا التابع <a data-ss1615747012="1" href="https://wiki.hsoub.com/React/react_component#render.28.29" rel="external">render</a>. سيعرّف التابع <code>render</code> كما توقعنا، كيف وماذا سيُعرض على الشاشة.
</p>

<p>
	لنعرّف حالة لقائمة الطرائف وللطرفة التي تُعرض حاليًا. بالمقارنة مع استخدام الخطاف <a data-ss1615747012="1" href="https://wiki.hsoub.com/React/hooks_state" rel="external">useState</a> فمكوّن الأصناف يحتوي فقط حالة واحدة. فإن تكوّنت الحالة من عدة أجزاء يجب حفظها كخصائص للحالة. سنهيّئ الحالة ضمن البانية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2161_9" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> extends </span><span class="typ">React</span><span class="pun">.</span><span class="typ">Component</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">state </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        anecdotes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
        current</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  render</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">anecdotes</span><span class="pun">.</span><span class="pln">length </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">no anecdotes</span><span class="pun">...&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">anecdote of the day</span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">anecdotes</span><span class="pun">[</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">current</span><span class="pun">].</span><span class="pln">content</span><span class="pun">}</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">next</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تخزّن حالة المكوّن في المتغيّر <code>this.state</code>. وستكون الحالة عبارة عن كائن بخاصيتين هما <code>this.state.anecdotes</code> وتمثل قائمة الطرائف و<code>this.state.current</code> والتي تخزن رقم الطرفة المعروضة.
</p>

<p>
	إنّ المكان الصحيح لإحضار البيانات من الخادم في مكوّنات الدوال هو داخل خطاف التأثير، والذي يُنفّذ عندما يصيّر المكوّن أو خلال فترات أبعد إن اقتضى الأمر، كالتصيير للمرة الأولى فقط.
</p>

<p>
	تقدّم <a data-ss1615747012="1" href="https://wiki.hsoub.com/React/state_and_lifecycle#.D8.A5.D8.B6.D8.A7.D9.81.D8.A9_.D8.AA.D9.88.D8.A7.D8.A8.D8.B9_.D8.AF.D9.88.D8.B1.D8.A9_.D8.A7.D9.84.D8.AD.D9.8A.D8.A7.D8.A9_.D8.A5.D9.84.D9.89_.D8.A7.D9.84.D8.B5.D9.86.D9.81" rel="external">توابع دورة العمل</a> في مكوّنات الأصناف وظائف مقابلة. وسيكون المكان الأمثل لإحضار البيانات من الخادم داخل تابع دور العمل <a data-ss1615747012="1" href="https://wiki.hsoub.com/React/react_component#componentDidMount.28.29" rel="external">componentDidMount</a>، والذي يُنفَّذ مرة واحدة عند كل تصيير للمكوّن:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2161_11" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> extends </span><span class="typ">React</span><span class="pun">.</span><span class="typ">Component</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">state </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      anecdotes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
      current</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  componentDidMount </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">    
      axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'http://localhost:3001/anecdotes'</span><span class="pun">)</span><span class="pln">
           </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">setState</span><span class="pun">({</span><span class="pln"> anecdotes</span><span class="pun">:</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">data </span><span class="pun">})</span><span class="pln">
      </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تُحدِّث دالة استدعاء طلب HTTP المكوّن باستخدام التابع <a data-ss1615747012="1" href="https://wiki.hsoub.com/React/react_component#setState.28.29" rel="external">setState</a>. يؤثر التابع فقط على المفاتيح التي عُرّفت في الكائن الذي سنمرّره إلى التابع كمعامل. وستبقى قيمة المفتاح <code>current</code> كما هي. كما يفعّل استدعاء التابع <code>setState</code> تابع التصيير <code>render</code>.
</p>

<p>
	سنكمل عملنا على المكوّن بإضافة إمكانية تغيير الطرفة المعروضة. تمثل الشيفرة التالية المكوّن بالكامل، ولوِّنت الشيفرة التي تنفذ الإضافة السابقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2161_13" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> extends </span><span class="typ">React</span><span class="pun">.</span><span class="typ">Component</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(</span><span class="pln">props</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">state </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      anecdotes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
      current</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  componentDidMount </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'http://localhost:3001/anecdotes'</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">setState</span><span class="pun">({</span><span class="pln"> anecdotes</span><span class="pun">:</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">data </span><span class="pun">})</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  handleClick </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">    </span><span class="kwd">const</span><span class="pln"> current </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="pln">
      </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">anecdotes</span><span class="pun">.</span><span class="pln">length
  </span><span class="pun">)</span><span class="pln">    
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">setState</span><span class="pun">({</span><span class="pln"> current </span><span class="pun">})</span><span class="pln">
                      </span><span class="pun">}</span><span class="pln">
  render</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">anecdotes</span><span class="pun">.</span><span class="pln">length </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">no anecdotes</span><span class="pun">...&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">anecdote of the day</span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">anecdotes</span><span class="pun">[</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">current</span><span class="pun">].</span><span class="pln">content</span><span class="pun">}&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">button onClick</span><span class="pun">={</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">handleClick</span><span class="pun">}&gt;</span><span class="pln">next</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وعلى سبيل المقارنة، فالشيفرة التالية هي الشيفرة المقابلة لمكوّنات الدوال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2161_15" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">anecdotes</span><span class="pun">,</span><span class="pln"> setAnecdotes</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">([])</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">[</span><span class="pln">current</span><span class="pun">,</span><span class="pln"> setCurrent</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useState</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">

  useEffect</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;{</span><span class="pln">
    axios</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'http://localhost:3001/anecdotes'</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      setAnecdotes</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">},[])</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> handleClick </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setCurrent</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">round</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">anecdotes</span><span class="pun">.</span><span class="pln">length </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)))</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">anecdotes</span><span class="pun">.</span><span class="pln">length </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">no anecdotes</span><span class="pun">...&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">anecdote of the day</span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">&gt;{</span><span class="pln">anecdotes</span><span class="pun">[</span><span class="pln">current</span><span class="pun">].</span><span class="pln">content</span><span class="pun">}&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">button onClick</span><span class="pun">={</span><span class="pln">handleClick</span><span class="pun">}&gt;</span><span class="pln">next</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إنّ الاختلاف بينهما ثانوي فيما يتعلق بتطبيقنا. أما الفرق الجوهري بين المكونين هو أنّ حالة مكوّن الأصناف هي كائن واحد، وأنّ التابع <code>setState</code> هو من يحدث الحالة. بينما يمكن في مكوّنات الدوال تخزين الحالة ضمن عدة متغيرات، لكل منها دالة تحديث خاصة بها.
</p>

<p>
	وعلى مستويات مقارنة أعلى، سيقدم خطاف التأثير آلية تحكم أفضل بالتأثيرات الجانبية مقارنة بتوابع دورة العمل التي تأتي مع مكوّنات الأصناف. والفائدة الملحوظة باستخدام مكوّنات الدوال تكمن في انعدام الحاجة لاستخدام المؤشر الذاتي <code>this</code> الذي تستخدمه أصناف JavaScript.
</p>

<p>
	لا توفر مكوّنات الأصناف بناء على رأي الكثيرين أية ميزات تفوق بها مكوّنات الدوال المدعّمة بالخطافات، ما عدا آلية <a data-ss1615747012="1" href="https://wiki.hsoub.com/React/error_boundaries" rel="external">محيط الخطأ</a> (error boundary) والتي لم تستخدم حتى الآن من قبل مكوّنات الدوال.
</p>

<p>
	<a data-ss1615747012="1" href="https://wiki.hsoub.com/React/hooks_faq#.D8.A3.D9.8A.D8.AA.D9.88.D8.AC.D8.A8_.D8.B9.D9.84.D9.8A_.D8.A7.D8.B3.D8.AA.D8.B9.D9.85.D8.A7.D9.84_.D8.A7.D9.84.D8.AE.D8.B7.D8.A7.D9.81.D8.A7.D8.AA.D8.8C_.D8.A3.D9.85_.D8.A7.D9.84.D8.A3.D8.B5.D9.86.D8.A7.D9.81.D8.8C_.D8.A3.D9.85_.D9.83.D9.84.D8.A7.D9.87.D9.85.D8.A7.D8.9F" rel="external">لا توجد أسباب منطقية تدفعك لاستخدام مكونات الأصناف</a> عند كتابة شيفرة لتطبيق جديد، إن كان مشروعك سيستخدم React 16.8 أو أعلى. و<a data-ss1615747012="1" href="https://wiki.hsoub.com/React/hooks_faq#.D9.87.D9.84_.D8.A7.D8.AD.D8.AA.D8.A7.D8.AC_.D8.A5.D9.84.D9.89_.D8.A5.D8.B9.D8.A7.D8.AF.D8.A9_.D9.83.D8.AA.D8.A7.D8.A8.D8.A9_.D8.AC.D9.85.D9.8A.D8.B9_.D9.85.D9.83.D9.88.D9.86.D8.A7.D8.AA_.D8.A7.D9.84.D8.A3.D8.B5.D9.86.D8.A7.D9.81_.D8.A7.D9.84.D8.AE.D8.A7.D8.B5.D8.A9_.D8.A8.D9.8A.D8.9F" rel="external">لا حاجة حاليًا لإعادة كتابة كل شيفرات React القديمة</a> كمكوّنات دوال.
</p>

<h2>
	تنظيم الشيفرة في تطبيقات React
</h2>

<p>
	لقد اتبعنا في معظم التطبيقات المبدأ التالي في التنظيم: حيث وضعنا المكوّنات في المجلد "components"، ووضعنا دوال الاختزال في المجلد"reducers"، وضعنا الشيفرة المسؤولة عن الاتصال مع الخادم في المجلد "services". تلائم الطريقة السابقة تنظيم التطبيقات الصغيرة. لكن عند زيادة عدد المكوّنات، سيتطلب الأمر حلولًا أفضل. ليست هنالك طريقة واحدة صحيحة لتنظيم المشروع، لكن قد تقدم لك المقالة التي تحمل العنوان <a data-ss1615747012="1" href="https://hackernoon.com/the-100-correct-way-to-structure-a-react-app-or-why-theres-no-such-thing-3ede534ef1ed" rel="external nofollow">The 100% correct way to structure a React app (or why there’s no such thing)</a> بعض الإضاءات بهذا الشأن.
</p>

<h2>
	الواجهة الخلفية والأمامية في المجلد نفسه
</h2>

<p>
	وضعنا حتى اللحظة الواجهتين الأمامية والخلفية في مستودعين منفصلين. وهذه المقاربة تقليدية جدًا. لكننا نقلنا الملف المُجمّع للواجهة الأمامية عند نشر التطبيق إلى مستودع الواجهة الخلفية. وربما يكون نشر شيفرة الواجهة الأمامية بشكل مستقل مقاربة أفضل، وخاصة للتطبيقات التي أنشئت باستخدام creat-react-app لأنها عملية سهلة التنفيذ بفضل المكتبة <a data-ss1615747012="1" href="https://github.com/mars/create-react-app-buildpack" rel="external nofollow">buildpack</a>.
</p>

<p>
	ربما ستصادفنا حالات تقتضي أن يوضع التطبيق بأكمله داخل مستودع واحد. في حالات كهذه، يشيع وضع الملفين في جذر المشروع، ووضع شيفرة الواجهتين الأمامية والخلفية في مجلدات خاصة بكل منهما مثل "client" و"server".
</p>

<p>
	يمكن الاطلاع على طريقة لتنظيم الأمور تستخدمها كنقطة انطلاق على <a data-ss1615747012="1" href="https://github.com/fullstack-hy2020/create-app" rel="external nofollow">GitHub</a>.
</p>

<h2>
	التغييرات على الخادم
</h2>

<p>
	إنّ حدثت أية تغيرات في حالة الخادم، كإضافة مدونة من قبل مستخدمين آخرين إلى خدمة قائمة المدونات، لن تتمكن الواجهة الأمامية التي طورناها خلال منهاجنا من تمييز هذه التغيرات حتى تُحمّل الصفحة من جديد. وتظهر المشكلة ذاتها عندما تُفعِّل الواجهة الأمامية عمليات حسابية مستهلكة للوقت ضمن الواجهة الخلفية. فكيف سنعكس نتائج العمليات على الواجهة الأمامية؟ إحدى الطرق المتبعة هي تنفيذ عملية انتخاب في الواجهة الأمامية، حيث تُكرر الطلبات إلى الواجهة البرمجية للواجهة الخلفية باستخدام الأمر <a data-ss1615747012="1" href="https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval" rel="external nofollow">setInterval</a> مثلًا.
</p>

<p>
	يمكن العمل بطريقة أكثر تعقيدًا باستخدام <a data-ss1615747012="1" href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" rel="external nofollow">مقابس الويب</a> (WebSockets) التي تؤمن قناة اتصال باتجاهين بين المتصفح والخادم. لا يضطر المتصفح في هذه الحالة إلى انتخاب الواجهة الخلفية، بل عليه فقط تعريف دوال استدعاء للحالات التي يرسل فيها الخادم بيانات تتعلق بتحديث الحالة مستخدمًا مقبس الويب.
</p>

<p>
	ومقابس الويب هي واجهات برمجية يزودنا بها المتصفح، لكنها غير مدعومة بشكل كامل من قبل جميع المتصفحات:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1615747012="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/web_socket_01.png.cf04564d2535b0d509acd4dc6bd99258.png" data-fileid="59678" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="59678" data-unique="lyruz9czq" src="https://academy.hsoub.com/uploads/monthly_2021_03/web_socket_01.png.cf04564d2535b0d509acd4dc6bd99258.png" alt="web_socket_01.png"></a>
</p>

<p>
	ينصح باستخدام المكتبة <a data-ss1615747012="1" href="https://socket.io/" rel="external nofollow">Socket.io</a> بدلًا من الاستخدام المباشر لواجهة مقبس الويب البرمجية. تؤمن هذه المكتبة عدة خيارات للتراجع إن لم يدعم المتصفح المقابس بشكل كامل.
</p>

<p>
	سنتعرف في القسم 8 على GraphQL والتي ستزودنا بآلية جيدة لتنبيه الزبون بأية تغييرات في الواجهة الخلفية.
</p>

<h2>
	DOM افتراضية
</h2>

<p>
	يظهر موضوع DOM الافتراضية عندما نناقش تقنيات React. ماذا تعنيه تلك العبارة؟ كما ذكرنا في القسم 0، تزودنا المتصفحات <a data-ss1615747012="1" href="https://developer.mozilla.org/fi/docs/DOM" rel="external nofollow">بواجهة برمجية لنموذج DOM</a>، والذي يمكِّن شيفرة JavaScript العاملة ضمن المتصفح من تغيير العناصر التي تحدد مظهر الصفحة.
</p>

<p>
	عندما يستخدم المطوّر React، فهو نادرًا ما يعدّل DOM مباشرة أو قد لا يعدلها أبدًا. فالدالة التي تعرّف كائن React يُعيد مجموعة من <a data-ss1615747012="1" href="https://wiki.hsoub.com/React/glossary#.D8.A7.D9.84.D8.B9.D9.86.D8.A7.D8.B5.D8.B1" rel="external">عناصر React</a>. على الرغم من أن بعضها يبدو كعناصر HTML عادية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2161_17" style="">
<span class="kwd">const</span><span class="pln"> element </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> world</span><span class="pun">&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span></pre>

<p>
	كما أنها أيضًا عناصر React مبنية على شيفرة JavaScript في صميمها.
</p>

<p>
	تُشكّل عناصر React التي تحدد مظهر المكونات في التطبيق <a data-ss1615747012="1" href="https://wiki.hsoub.com/React/faq_internals#.D9.85.D8.A7_.D9.87.D9.88_DOM_.D8.A7.D9.84.D8.A7.D9.81.D8.AA.D8.B1.D8.A7.D8.B6.D9.8A_.28Virtual_DOM.29.D8.9F" rel="external">DOM الافتراضية</a>، والتي تُخزَّن في ذاكرة النظام أثناء تشغيل التطبيق.
</p>

<p>
	وتصيّر DOM الافتراضية بمساعدة المكتبة <a data-ss1615747012="1" href="https://wiki.hsoub.com/React/react_dom" rel="external">ReactDOM</a> إلى DOM الحقيقية التي يمكن للمتصفح عرضها باستخدام "DOM <abbr title="Application Programming Interface | واجهة برمجية">API</abbr>".
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2161_19" style="">
<span class="typ">ReactDOM</span><span class="pun">.</span><span class="pln">render</span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">App</span><span class="pln"> </span><span class="pun">/&gt;,</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">'root'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">)</span></pre>

<p>
	عندما تتغير حالة التطبيق تُعرَّف DOM افتراضية جديدة بواسطة المكوّنات. تمتلك React النسخة السابقة منها في ذاكرة النظام، وبدلًا من تصيير النسخة الجديدة مباشرة باستخدام "DOM <abbr title="Application Programming Interface | واجهة برمجية">API</abbr>"، تقدر React الطريقة المثلى لتحديث DOM (إزالة أو إضافة أو تعديل العناصر ضمن DOM). وهكذا ستعكس DOM النسخة الجديدة دائمًا.
</p>

<h2>
	دور React في التطبيقات
</h2>

<p>
	ربما لم نظهر بوضوح خلال تقدمنا في المنهاج أنّ React هي بشكل أساسي مكتبة لإدارة إنشاء واجهات عرض للتطبيقات. فلو نظرنا إلى النمط التقليدي <a data-ss1615747012="1" href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller" rel="external nofollow">للمتحكم بنموذج العرض</a>(MVC-Model View Controller)، فسيكون نطاق استخدام React هو العرض(View). وللمكتبة React مجال تطبيق أضيق من <a data-ss1615747012="1" href="https://angular.io/" rel="external nofollow">Angular</a> على سبيل المثال، والتي تمثل إطار عمل للتحكم بوحدة عرض الواجهات الأمامية. وهكذا لا تمثل React ما ندعوه "إطار عمل"، بل هي مكتبة.
</p>

<p>
	تخزّن البيانات التي يتعامل معها تطبيق React في حالة مكوناته، فيمكننا إذًا التفكير بحالة التطبيق على أنها نموذج من معمارية MVC.
</p>

<p>
	لا نشير عادة إلى معمارية MVC عند الحديث عن تطبيقات React. فلو استخدمنا Redux فسيتبع التطبيق معمارية <a data-ss1615747012="1" href="https://facebook.github.io/flux/docs/in-depth-overview" rel="external nofollow">Flux</a> وسيركّز دور React هنا أكثر على إنشاء واجهات العرض. إذ ستعالج حالة Redux ومولدات الأفعال منطق التطبيق. أما عند استخدام <a data-ss1615747012="1" href="https://academy.hsoub.com/programming/javascript/react/%D8%A7%D9%84%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-%D9%85%D8%B9%D8%AA%D9%85%D8%AF-%D8%B9%D9%84%D9%89-redux-r1164/" rel="">redux thunk</a> التي تعرفنا عليها في القسم 6، فسنجد أنّ منطق التطبيق سينفصل كليًا عن شيفرة React.
</p>

<p>
	وطالما أن React وFlux من إنتاج Facebook، فيمكننا القول أنّ استخدام React كمكتبة للتعامل مع واجهة المستخدم (UI) هي الطريقة التي ينبغي استخدامها، كما أن توافقها مع معمارية Flux سيضيف ميزة إلى التطبيق. لكن لو تحدثنا عن التطبيقات الصغيرة أو النماذج الأولية سيكون من المفيد استعمال React بطريقة "خاطئة" غير التي صممت لأجلها، ذلك أنّ <a data-ss1615747012="1" href="https://en.wikipedia.org/wiki/Overengineering" rel="external nofollow">تجاوز الحدود التقنية</a> نادرًا ما يثمر.
</p>

<p>
	وكما أشرنا في نهاية الفصل الأخير من القسم 6، تقدم React الواجهة البرمجية والتي تمثل حلًا بديلًا لمركزية إدارة الحالة دون استخدام طرف ثالث مثل Redux. يمكنك الاطلاع على المقالة <a data-ss1615747012="1" href="https://www.simplethread.com/cant-replace-redux-with-hooks" rel="external nofollow">?can't replace redux with hooks</a> والمقالة <a data-ss1615747012="1" href="https://hswolff.com/blog/how-to-usecontext-with-usereducer" rel="external nofollow">?how to usecontext with usereducer</a>
</p>

<h2>
	أمن تطبيقات React/Node
</h2>

<p>
	لم نتطرق حتى الآن في مادة المنهاج إلى أمن المعلومات. وليس لدينا الوقت الكافي أيضًا. لكن لحسن الحظ قسم علوم الحاسب في جامعة هلسنكي يقدم المنهاج <a data-ss1615747012="1" href="https://cybersecuritybase.mooc.fi/module-2.1" rel="external nofollow">Securing Software</a> لهذا الغرض.
</p>

<p>
	ينشر المشروع المفتوح لأمن تطبيقات الويب <a data-ss1615747012="1" href="https://www.owasp.org/" rel="external nofollow">OWASP</a> قائمة سنوية بأكثر الأخطار الأمنية شيوعًا في تطبيقات الويب. ستجد اللائحة الحالية على الموقع <a data-ss1615747012="1" href="https://owasp.org/www-project-top-ten." ipsnoembed="false" rel="external nofollow">https://owasp.org/www-project-top-ten.</a> ويمكن أن تتكرر الأخطار ذاتها سنويًا.
</p>

<p>
	ستتصدر القائمة أخطار الإدخالات المشبوهة (injection). ويعني ذلك تفسير النص الذي أرسل عبر استمارة تطبيق بشكل مختلف كليًا عما أراده المطوّر. وأكثر هذه الإدخالات شهرة هي <a data-ss1615747012="1" href="https://stackoverflow.com/questions/332365/how-does-the-sql-injection-from-the-bobby-tables-xkcd-comic-work" rel="external nofollow">إدخالات SQL</a>
</p>

<p>
	لنفترض على سبيل المثال، أنّ استعلام SQL التالي سيُنفّذ ضمن تطبيق قابل للاختراق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2161_21" style="">
<span class="pln">let query </span><span class="pun">=</span><span class="pln"> </span><span class="str">"SELECT * FROM Users WHERE name = '"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> userName </span><span class="pun">+</span><span class="pln"> </span><span class="str">"';"</span></pre>

<p>
	لنفترض الآن أنّ المستخدم المشبوه Arto Hellas سيعرّف اسمه بالشكل التالي:
</p>

<pre class="ipsCode">
Arto Hell-as'; DROP TABLE Users; --
</pre>

<p>
	سيحوي الاسم علامة التنصيص المفردة <code>'</code> والتي تمثل محرف بداية ونهاية السلسلة النصية في لغة SQL. وكنتيجة لتنفيذ عبارتي SQL السابقتين، ستدمر العبارة الثانية الجدول <code>Users</code> من قاعدة البيانات.
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_2161_24" style="">
<span class="pln">SELECT </span><span class="pun">*</span><span class="pln"> FROM </span><span class="typ">Users</span><span class="pln"> WHERE name </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Arto Hell-as'</span><span class="pun">;</span><span class="pln"> DROP TABLE </span><span class="typ">Users</span><span class="pun">;</span><span class="pln"> </span><span class="pun">--</span><span class="str">'</span></pre>

<p>
	يُمنع هذا النوع من الأخطار <a data-ss1615747012="1" href="https://security.stackexchange.com/questions/172297/sanitizing-input-for-parameterized-queries" rel="external nofollow">بتطهير</a> النص المدخل، وذلك بالتحقق أنّ الاستعلام لا يحتوي على محارف ممنوعة الاستخدام، كعلامة التنصيص المفردة في حالتنا. فإن وجدت مثل هذه المحارف ستستبدل ببدائل آمنة لها باستخدام <a data-ss1615747012="1" href="https://en.wikipedia.org/wiki/Escape_character#JavaScript" rel="external nofollow">محارف الهروب</a>.
</p>

<p>
	يمكن أن يحصل هذا النوع من الهجوم على قواعد بيانات لا تستخدم SQL. فقاعدة البيانات Mongoose تحبط هذا الهجوم بعملية <a data-ss1615747012="1" href="https://zanon.io/posts/nosql-injection-in-mongodb" rel="external nofollow">تطهير</a> الاستعلام. يمكنك الاطلاع على المزيد حول هذا الموضوع على الموقع <a data-ss1615747012="1" href="https://blog.websecurify.com/2014/08/hacking-nodejs-and-mongodb.html" rel="external nofollow">blog.websecurify.com/2014/08/hacking-nodejs-and-mongodb.html</a>.
</p>

<p>
	من الأخطار الأخرى هي السكربت العابرة للمواقع (Cross-sites scripting XSS). إذ يمكن لهذا الهجوم أن يدخل شيفرة JavaScript مشبوهة إلى تطبيق ويب شرعي. ستُنفَّذ عندها هذه الشيفرة في متصفح الضحية. فلو حاولنا إدخال الشيفرة التالية داخل تطبيق الملاحظات:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2161_27" style="">
<span class="tag">&lt;script&gt;</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">'Evil XSS attack'</span><span class="pun">)</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	لن يتم تنفيذ الشيفرة بل ستصيّر على شكل نص على الصفحة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1615747012="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/xss_attack_note_app_02.png.3f1db655cb88cecac21f6a1d0ee30ee9.png" data-fileid="59679" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="59679" data-unique="h1bm1wojj" src="https://academy.hsoub.com/uploads/monthly_2021_03/xss_attack_note_app_02.png.3f1db655cb88cecac21f6a1d0ee30ee9.png" alt="xss_attack_note_app_02.png"></a>
</p>

<p>
	ذلك أنّ React <a data-ss1615747012="1" href="https://wiki.hsoub.com/React/introducing_jsx#.D8.AA.D9.85.D9.86.D8.B9_JSX_.D9.87.D8.AC.D9.85.D8.A7.D8.AA_.D8.A7.D9.84.D8.AD.D9.82.D9.86" rel="external">تُطهّر البيانات التي تحملها المتغيرات</a>. لقد كانت بعض نسخ React <a data-ss1615747012="1" href="https://medium.com/dailyjs/exploiting-script-injection-flaws-in-reactjs-883fb1fe36c1" rel="external nofollow">عرضة لهجوم</a> XSS. بالطبع تم سد هذه الثغرات، لكن لا أحد يضمن ما سيحدث.
</p>

<p>
	على المطور أن يبقى حذرًا عند استخدام المكتبات، وعليه أن يحدّث مكتباته بشكل مستمر إن ظهرت لها تحديثات أمنية جديدة. ستجد التحديثات المتعلقة بالمكتبة Express ضمن <a data-ss1615747012="1" href="https://expressjs.com/en/advanced/security-updates.html" rel="external nofollow">توثيق المكتبة</a>، بينما ستجد تحديثات Node ضمن المدونة <a data-ss1615747012="1" href="https://nodejs.org/en/blog." ipsnoembed="false" rel="external nofollow">https://nodejs.org/en/blog.</a>
</p>

<p>
	كما يمكنك التحقق من وضع اعتمادياتك باستخدام الأمر:
</p>

<pre class="ipsCode">
npm outdated --depth 0
</pre>

<p>
	لقد احتوت الحلول النموذجية لتمرينات القسم 4 بعض الاعتماديات التي احتاجت إلى تحديث العام الماضي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1615747012="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/outdate_depend_03.png.1f31dcc63d3ff3912af702b3829a447a.png" data-fileid="59676" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="59676" data-unique="n9ikkdvof" src="https://academy.hsoub.com/uploads/monthly_2021_03/outdate_depend_03.png.1f31dcc63d3ff3912af702b3829a447a.png" alt="outdate_depend_03.png"></a>
</p>

<p>
	يمكن تحديث الاعتمادية بتحديث الملف "package.json" وتنفيذ الأمر <code>npm install</code>. وانتبه إلى أنّ النسخ القديمة من الاعتماديات ليست بالضرورة خطرًا أمنيًا.
</p>

<p>
	يمكن أن تستخدم الأمر <a data-ss1615747012="1" href="https://docs.npmjs.com/cli/audit" rel="external nofollow">audit</a> للتحقق من أمن الاعتماديات. حيث يقارن أرقام نسخة الاعتمادية في تطبيقك مع قائمة بأرقام النسخ التي ثَبُت احتواؤها على تهديد أمني ضمن قاعدة بيانات مركزية.
</p>

<p>
	إنّ تنفيذ الأمر <code>npm audit</code> على تمرين من تمرينات القسم الرابع من منهاج العام الفائت سيطبع لك قائمة طويلة بالمشاكل وطريقة حلها، والتقرير التالي هو جزء من التقرير الكلي الناتج:
</p>

<pre class="ipsCode">
$ bloglist-backend npm audit

                       === npm audit security report ===

# Run  npm install --save-dev jest@25.1.0  to resolve 62 vulnerabilities
SEMVER WARNING: Recommended action is a potentially breaking change
┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Low           │ Regular Expression Denial of Service                         │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package       │ braces                                                       │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ jest [dev]                                                   │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path          │ jest &gt; jest-cli &gt; jest-config &gt; babel-jest &gt;                 │
│               │ babel-plugin-istanbul &gt; test-exclude &gt; micromatch &gt; braces   │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info     │ https://npmjs.com/advisories/786                             │
└───────────────┴──────────────────────────────────────────────────────────────┘


┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Low           │ Regular Expression Denial of Service                         │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package       │ braces                                                       │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ jest [dev]                                                   │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path          │ jest &gt; jest-cli &gt; jest-runner &gt; jest-config &gt; babel-jest &gt;   │
│               │ babel-plugin-istanbul &gt; test-exclude &gt; micromatch &gt; braces   │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info     │ https://npmjs.com/advisories/786                             │
└───────────────┴──────────────────────────────────────────────────────────────┘


┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Low           │ Regular Expression Denial of Service                         │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package       │ braces                                                       │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ jest [dev]                                                   │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path          │ jest &gt; jest-cli &gt; jest-runner &gt; jest-runtime &gt; jest-config &gt; │
│               │ babel-jest &gt; babel-plugin-istanbul &gt; test-exclude &gt;          │
│               │ micromatch &gt; braces                                          │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info     │ https://npmjs.com/advisories/786                             │
└───────────────┴──────────────────────────────────────────────────────────────┘

...


found 416 vulnerabilities (65 low, 2 moderate, 348 high, 1 critical) in 20047 scanned packages
  run `npm audit fix` to fix 354 of them.
  62 vulnerabilities require semver-major dependency updates.
</pre>

<p>
	فخلال عام فقط، امتلأ التطبيق بالتهديدات الأمنية الصغيرة. ولحسن الحظ لم يكن هنالك سوى تهديد خطير واحد. لننفذ الأمر <code>npm audit fix</code> كما يوصي التقرير:
</p>

<pre class="ipsCode">
$ bloglist-backend npm audit fix

+ mongoose@5.9.1
added 19 packages from 8 contributors, removed 8 packages and updated 15 packages in 7.325s
fixed 354 of 416 vulnerabilities in 20047 scanned packages
  1 package update for 62 vulns involved breaking changes
  (use `npm audit fix --force` to install breaking changes; or refer to `npm audit` for steps to fix these manually)
</pre>

<p>
	بقيت التهديدات لأن الإصلاح الافتراضي باستخدام <code>audit</code> لا يحدّث الاعتماديات إن زاد الرقم الأساسي لنسخها عن 62. وتحديث هذه الاعتماديات قد يسبب انهيارًا كاملًا في التطبيق. نتجت بقية التهديدات عن اعتمادية التطوير Jest. فرقم نسخة التطبيق هو 23.6.0 بينما تحمل النسخة الآمنة الرقم 25.0.1. وطالما أنها اعتمادية تطوير فلن يكون التهديد موجودًا أصلًا. مع ذلك سنحدّث المكتبة لنبقى في دائرة الأمان:
</p>

<pre class="ipsCode">
npm install --save-dev jest@25.1.0 
</pre>

<p>
	سيبدو الوضع جيدًا بعد التحديث:
</p>

<pre class="ipsCode">
 $ blogs-backend npm audit

                       === npm audit security report ===

found 0 vulnerabilities
 in 1204443 scanned packages
</pre>

<p>
	من التهديدات الأخرى المذكورة في قائمة OWASP، سنجد إخفاق التحقق (Broken Authentication) وإخفاق التحكم بالوصول (Broken Access Control). إنّ التحقق المبني على الشهادات الذي استخدمناه قوي بما يكفي إن استخدم مع بروتوكول النقل المشفّر HTTPS. لذلك عندما نضيف ميزة التحكم بالوصول إلى التطبيق، ينبغي أن لا نكتفي بالتحقق من هوية المستخدم على المتصفح، بل على الخادم أيضًا. فمن الأخطاء الأمنية، منع بعض الأفعال من الحدوث بإخفاء خيارات التنفيذ في الشيفرة التي ينفذها المتصفح فقط.
</p>

<p>
	ستجد دليلًا جيدًا جدًا عن <a data-ss1615747012="1" href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/First_steps/Website_security" rel="external nofollow">أمن مواقع الويب</a> على موقع Mozilla's MDN، والذي سيثير الموضوع المهم التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1615747012="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/security_issue_04.png.27a696fb8a5a0e6c7f8f301d442aed97.png" data-fileid="59677" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="59677" data-unique="bcgoh3fkf" src="https://academy.hsoub.com/uploads/monthly_2021_03/security_issue_04.png.27a696fb8a5a0e6c7f8f301d442aed97.png" alt="security_issue_04.png"></a>
</p>

<p>
	يحتوي توثيق Express على فصل حول موضوع الأمن بعنوان <a data-ss1615747012="1" href="https://expressjs.com/en/advanced/best-practice-security.html" rel="external nofollow">Production Best Practices: Security</a> من المفيد الاطلاع عليه. كما ننصحك بإضافة مكتبة تدعى <a data-ss1615747012="1" href="https://helmetjs.github.io/" rel="external nofollow">Helmet</a> إلى الواجهة الخلفية. حيث تحتوي هذه المكتبة على أدوات وسطية تزيل بعض الثغرات الأمنية في تطبيقات Express. كما تستحق الأداة <a data-ss1615747012="1" href="https://github.com/nodesecurity/eslint-plugin-security" rel="external nofollow">security-plugin</a> العائدة للمدقق ESlint التجربة.
</p>

<h2>
	المسارات الحالية للتطور
</h2>

<p>
	لنتحدث أخيرًا عن بعض التكنولوجيا المستقبلية (الحالية في الواقع)، والمسارات التي يسلكها تطور الويب.
</p>

<h3>
	نسخ بمتغيرات نمطية من JavaScript
</h3>

<p>
	إنّ <a data-ss1615747012="1" href="https://developer.mozilla.org/en-US/docs/Glossary/Dynamic_typing" rel="external nofollow">التحقق الديناميكي من أنماط</a> المتغيرات في JavaScript قد يسبب بعض التغيرات المزعجة. فلقد تحدثنا في القسم 5 بإيجاز عن الخصائص النمطية وهي آلية تمكن المطوّر من التحقق من نمط الخاصية التي ستُمرّر إلى مكوّن React.
</p>

<p>
	زاد الاهتمام مؤخرًا بالتحقق الساكن من الأنماط. وتعتبر نسخة المتغيرات النمطية من JavaScript التي قدمتها Microsoft باسم <a data-ss1615747012="1" href="https://wiki.hsoub.com/TypeScript/" rel="external">Typescript</a> الأكثر شعبية، وسنتعرف عليها في القسم 9.
</p>

<h3>
	التصيير من جهة الخادم والتطبيقات الإيزومورفية والشيفرة الموّحدة
</h3>

<p>
	لا تصيّر مكونات React في نطاق المتصفحات فقط. إذ يمكن أن ينجز التصيير ضمن <a data-ss1615747012="1" href="https://wiki.hsoub.com/React/react_dom_server" rel="external">الخادم</a>. يزداد استخدام هذه المقاربة بحيث يقدم الخادم صفحة مصيّرة مسبقًا باستخدام React عند الدخول إلى التطبيق للمرة الأولى. ومن ثم تُستأنف العمليات بالشكل المعتاد، أي سينفذ المتصفح منطق React الذي يغيّر في DOM التي سيعرضها المتصفح.
</p>

<p>
	تدعى عملية التصيير على الخادم بالاسم "التصيير ضمن الخادم".
</p>

<p>
	إنّ إحدى دوافع التصيير ضمن الخادم هو استمثال محركات البحث (SEO). فلطالما كانت محركات البحث سيئة في تمييز المحتوى الناتج عن تصيير شيفرة JavaScript. لكن على ما يبدو أنّ هذا الأمر في انحسار. يمكنك الاطلاع على ذلك بزيارة المقالين <a data-ss1615747012="1" href="https://www.javascriptstuff.com/react-seo" rel="external nofollow">Will Google find your React content?‎</a> و <a data-ss1615747012="1" href="https://medium.freecodecamp.org/seo-vs-react-is-it-neccessary-to-render-react-pages-in-the-backend-74ce5015c0c9" rel="external nofollow">SEO vs. React: Web Crawlers are Smarter Than You Think</a>.
</p>

<p>
	وبالطبع لا يقتصر التصيير ضمن المخدم على شيفرة React أو JavaScript. فاستخدامنا لنفس لغة البرمجة ضمن منهاجنا من حيث المبدأ، هو لتبسيط تنفيذ المفاهيم، إذ أنّ اللغة نفسها ستعمل على الواجهتين الخلفية والأمامية.
</p>

<p>
	إلى جانب التصيير ضمن الخادم، تدور الأحاديث عن ما يسمى بالتطبيقات الإيزومورفية والشيفرة الموحدة، على الرغم من الجدل القائم حول تعريفهما. واستنادًا إلى بعض التعاريف فتطبيق الويب الإيزومورفي، هو التطبيق الذي يُصيّر ضمن كلتا الواجهتين الأمامية والخلفية. بينما تُعرّف الشيفرة الموحدة بأنها شيفرة قابلة للتنفيذ في معظم بيئات التشغيل، أي الواجهتين الأمامية والخلفية.
</p>

<p>
	تمنحنا React وNode خيارات مرغوبة في كتابة تطبيقات ايزومورفية بشيفرة موحدة.
</p>

<p>
	إنّ كتابة شيفرة موحدة باستخدام React عملية صعبة. لكن أثارت مؤخرًا المكتبة <a data-ss1615747012="1" href="https://github.com/zeit/next.js/" rel="external nofollow">Next.js</a> التي تًدرج أعلى تطبيق React، الكثير من الاهتمام، وتعتبر خيارًا جيدًا في كتابة تطبيقات موحّدة.
</p>

<h3>
	تطبيقات الويب العصرية
</h3>

<p>
	بدأ المطورون باستخدام مصطلح <a data-ss1615747012="1" href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%85%D9%8A%D8%A9-pwa-r832/" rel="">تطبيقات الويب العصرية</a> (progressive web app - PWA) الذي أطلقه Google. ونتحدث باختصار عن تطبيقات الويب التي تعمل بأفضل شكل ممكن على كل المنصات مستفيدة من الميزات الأبرز لعناصر المنصة. ولا يجب أن يحد حجم شاشة الأجهزة النقالة من القدرة على استعمال هذه التطبيقات. كما يجب أن تعمل هذه التطبيقات بلا مشاكل دون اتصال أو مع الاتصالات البطيئة بالإنترنت. وينبغي أيضًا أن تُثبّت على الأجهزة النقالة كأي تطبيق آخر. وأخيرًا يجب أن تكون جميع البيانات المنقولة عبرها مشفّرة.
</p>

<p>
	تعتبر التطبيقات التي تُنشئها الأداة create-react-app <a data-ss1615747012="1" href="https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#making-a-progressive-web-app" rel="external nofollow">عصرية</a> افتراضيًا. لكن سيتطلب جعل التطبيق الذي يستخدم بيانات مصدرها الخادم عصريًا جهدًا. كما تنجز وظيفة العمل دون اتصال بالاستفادة من الواجهة البرمجية <a data-ss1615747012="1" href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API" rel="external nofollow">service workers</a>.
</p>

<h3>
	معمارية الخدمات الدقيقة
</h3>

<p>
	لقد تعلمنا خلال منهاجنا بعض المواضيع السطحية فقط عن التعامل مع الخادم. فلقد اعتمدت تطبيقاتنا على واجهة خلفية متراصة، بمعنى أنها تتألف من تطبيق واحد يدير كل شيء، ويعمل على خادم واحد، ويؤمن واجهة برمجية تخدم عدة زبائن.
</p>

<p>
	لكن ستسبب الواجهة الخلفية المتراصة المشاكل عندما ينمو التطبيق على صعيدي الأداء والصيانة.
</p>

<p>
	تعتبر <a data-ss1615747012="1" href="https://martinfowler.com/articles/microservices.html" rel="external nofollow">المعمارية الدقيقة</a> (الخدمات الدقيقة-microservices) طريقة لكتابة الواجهة الخلفية لتطبيق على شكل عدة خدمات منفصلة ومستقلة تتواصل مع بعضها عبر شبكة الاتصال. وتكون الغاية من كل خدمة إدارة منطق وظيفة محددة بالكامل. ولا تستخدم الخدمات في معمارية الخدمات الدقيقة النقيّة أية قواعد بيانات مشتركة.
</p>

<p>
	فيمكن لتطبيق قائمة المدونات على سبيل المثال أن يتكون من خدمتين: تتعامل الأولى مع المستخدمين والأخرى مع المدونات. وستكون مسؤولية الوظيفة الأولى تسجيل دخول المستخدم والتحقق منه، بينما ستهتم الثانية بمنطق التعامل مع المدونات.
</p>

<p>
	يوضح الشكل التالي الاختلاف بين بنية تطبيق تعتمد على معمارية الخدمات الدقيقة وآخر يعتمد على بنية تقليدية متراصة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-ss1615747012="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/microsevice_vs_momolithic_05.png.283f0236a85ad6c503a8b0b6a86322de.png" data-fileid="59675" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="59675" data-unique="z6ekqastp" src="https://academy.hsoub.com/uploads/monthly_2021_03/microsevice_vs_momolithic_05.png.283f0236a85ad6c503a8b0b6a86322de.png" alt="microsevice_vs_momolithic_05.png"></a>
</p>

<p>
	لا يختلف دور الواجهة الأمامية (محاطة بمربع في الشكل السابق) كثيرًا بين النموذجين. وقد يتواجد أحيانًا ما يسمى <a data-ss1615747012="1" href="http://microservices.io/patterns/apigateway" rel="external nofollow">بوابة الواجهة البرمجية</a> (<abbr title="Application Programming Interface | واجهة برمجية">API</abbr> gateway) بين الخدمات الدقيقة والواجهة الأمامية، والتي تعطي انطباعًا بأنها بنية تقليدية، "حيث يوجد كل شيء على الخادم نفسه". تستخدم <a data-ss1615747012="1" href="https://medium.com/netflix-techblog/optimizing-the-netflix-api-5c9ac715cf19" rel="external nofollow">Netflix</a> وغيرها مقاربة مماثلة
</p>

<p>
	لقد تطورت معمارية الخدمات المصغرة لتلبي احتياجات تطبيقات الإنترنت واسعة النطاق. ولقد اعتمدت Amazon هذه الفكرة قبل أن يظهر مصطلح الخدمات الدقيقة بمدة طويلة. وكانت نقطة البدء الحاسمة بريدًا إلكترونيًا أرسله "جيف بيزوس" المدير التنفيذي لشركة Amazon إلى جميع الموظفين عام 2020:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		على جميع الفرق عرض بياناتها ووظائفها من خلال واجهات خدمية من الآن فصاعدًا.
	</p>

	<p>
		على الفرق أن تتواصل مع بعضها من خلال هذه الواجهات.
	</p>

	<p>
		لن يسمح باستخدام أي شكل من أشكال التواصل الداخلي: لا روابط مباشرة، لا مزيد من قراءة مخزن بيانات الفرق الأخرى، لا نماذج لاستخدام ذاكرة مشتركة، لا أبواب خلفية أيًا كانت الغاية منها. التواصل الوحيد المسموح سيكون عبر طلب الواجهات من خلال شبكة الاتصال.
	</p>

	<p>
		لا يهم أية تقنيات ستستخدم.
	</p>

	<p>
		يجب أن تصمم كل الواجهات الخدمية دون استثناء من الصفر حتى النقطة التي يمكن فيها استخدامها خارج إطار الشبكة الداخلية. وفحوى الحديث، أنّ المطلوب من كل فريق أن يخطط ويصمم واجهته بحيث يمكن تصديرها إلى مطورين خارج نطاق الشركة.
	</p>

	<p>
		لا استثناءات
	</p>

	<p>
		سيُطرد كل موظف لم ينفذ المطلوب! أتمنى لكم نهارًا سعيدًا.
	</p>
</blockquote>

<p>
	تعتبر حاليًا <a data-ss1615747012="1" href="https://www.infoq.com/presentations/netflix-chaos-microservices" rel="external nofollow">Netflix</a> من الشركات الرائدة في استخدام الخدمات الدقيقة. تأخذ هذه الخدمات وبشكل تدريجي ومستقر طابع <a data-ss1615747012="1" href="https://en.wikipedia.org/wiki/No_Silver_Bullet" rel="external nofollow">الطلقة الرابحة</a>، بمعنى أنها تُوصف كحل لكل المشاكل تقريبًا. لكن بالطبع هنالك العديد من المشاكل عند تطبيق معماريتها، ومن المنطقي أن نجرب <a data-ss1615747012="1" href="https://martinfowler.com/bliki/MonolithFirst.html" rel="external nofollow">البنية المتراصة أولًا</a> ببناء واجهة خلفية تضم كل شيء كبداية. وربما <a data-ss1615747012="1" href="https://martinfowler.com/articles/dont-start-monolith.html" rel="external nofollow">لا</a>.
</p>

<p>
	تضاربت الآراء حول الموضوع. ويستشهد كلا الطرفين بما ورد في موقع "مارتن فولر"،فحتى الخبراء لا يمكنهم الجزم أي الطريقتين هي الأصح. لسوء الحظ لا يمكننا الغوص عميقًا في هذه الفكرة المهمة، وحتى لو أردنا إلقاء نظرة سريعة عليها فقد تستغرق 5 أسابيع أخرى.
</p>

<h3>
	الاستقلال عن الخوادم
</h3>

<p>
	بدأت معالم مسار جديد في تطوير تطبيقات الويب بالتكوّن، بعد إصدار الخدمة السحابية <a data-ss1615747012="1" href="https://aws.amazon.com/lambda/" rel="external nofollow">lambda</a> من قبل Amazon عام 2014. إنّ المعلم الرئيسي في lambda وفي <a data-ss1615747012="1" href="https://cloud.google.com/functions/" rel="external nofollow">الوظائف السحابية</a> لشركة Google، وكذلك ا<a data-ss1615747012="1" href="https://azure.microsoft.com/en-us/services/functions/" rel="external nofollow">لوظائف السحابية على Azure</a> أنها قادرة على تنفيذ وظائف فردية في السحابة. وقد كانت أصغر وحدة قابلة للتنفيذ على السحابة هي عملية مفردة، أي بيئة تشغيل تعتمد على Node كواجهة خلفية.
</p>

<p>
	يمكن على سبيل المثال تصميم تطبيقات مستقلة عن الخادم (serverless) باستخدام <a data-ss1615747012="1" href="https://aws.amazon.com/api-gateway/" rel="external nofollow">بوابة الواجهة البرمجية</a> التي تقدمها Amazon. حيث تستجيب الوظائف السحابية على الطلب المرسل إلى الواجهة البرمجية التي تدير طلبات HTTP. حيث تعمل الوظائف عادة باستخدام البيانات المخزّنة ضمن قاعدة بيانات الخدمة السحابية.
</p>

<p>
	لا يعني الاستقلال عن الخوادم عدم وجود خادم في التطبيقات، لكن كيفية تعريف الخادم. فيمكن لمطوري البرمجيات نقل جهودهم البرمجية إلى مستوى أعلى من التجريد، عندما لا تكون هنالك حاجة لتحديد مسارات طلبات HTTP برمجيًا، أو تحديد العلاقات بين قواعد البيانات وغير ذلك، طالما أن البنية التحتية للسحابة ستؤمن كل ذلك. تستطيع الوظائف السحابية إنشاء ودعم الأنظمة واسعة النطاق. فيمكن للخدمة السحابية Lambda تنفيذ كمية هائلة من الوظائف السحابية في الثانية. ويحدث كل ذلك تلقائيًا ضمن البنية التحتية، ولا حاجة لتهيئة أية خوادم إضافية.
</p>

<h3>
	مكتبات مفيدة وروابط مهمة
</h3>

<p>
	أنتج مجتمع تطوير JavaScript عددًا ضخمًا من المكتبات المتنوعة المفيدة. فإن كنت بصدد تطوير أي شيء أكثر أهمية، تحقق من وجود حلول جاهزة متاحة، ويمكنك أن تجد الكثير من المكتبات في الموقع <a data-ss1615747012="1" href="https://applibslist.xyz." ipsnoembed="false" rel="external nofollow">https://applibslist.xyz.</a> وستجد في آخر الفقرة بعض المكتبات التي ينصح بها شركاء موثوقين.
</p>

<p>
	إن رأيت أن تطبيقك سيعالج بيانات معقدة، فالمكتبة <a data-ss1615747012="1" href="https://www.npmjs.com/package/lodash" rel="external nofollow">lodash</a> التي نصحنا بها في <a data-ss1615747012="1" href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%87%D9%8A%D9%83%D9%84-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%AE%D9%84%D9%81%D9%8A%D8%A9-%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-unit-tests-r1132/" rel="">القسم 4</a>، من المكتبات الجيدة. وإن كنت تفضل البرمجة باستخدام الدوال يمكنك أن تحاول مع المكتبة <a data-ss1615747012="1" href="https://ramdajs.com/" rel="external nofollow">ramda</a>.
</p>

<p>
	إن كنت تتعامل مع الوقت والتاريخ فالمكتبة <a data-ss1615747012="1" href="https://github.com/date-fns/date-fns" rel="external nofollow">date-fns</a> أداة جيدة.
</p>

<p>
	تساعدك المكتبتان <a data-ss1615747012="1" href="https://www.npmjs.com/package/formik" rel="external nofollow">Formik</a> و<a data-ss1615747012="1" href="https://redux-form.com/8.3.0/" rel="external nofollow">redux-form</a> في معالجة النماذج بطريقة أسهل. وإن كنت ستستخدم الرسوميات في تطبيقك فهناك خيارات عدة. وننصحك باستخدام <a data-ss1615747012="1" href="http://recharts.org/en-US/" rel="external nofollow">recharts</a> و<a data-ss1615747012="1" href="https://github.com/highcharts/highcharts-react" rel="external nofollow">highcharts</a>.
</p>

<p>
	تؤمن المكتبة <a data-ss1615747012="1" href="https://github.com/facebook/immutable-js/" rel="external nofollow">immutable.js</a> التي طورتها Facebook، وكما يوحي اسمها، إدراج بعض بنى البيانات الثابتة. إذ يمكن استخدام المكتبة عند استخدام Redux، لأنه وكما أشرنا في القسم 6، أن دوال الاختزال يجب أن تكون دوال نقية، بمعنى أنها لن تعدِّل حالة مخزن Redux، بل ستستبداله بآخر جديد عندما تحدث أية تغييرات. لقد أثر ظهور المكتبة <a data-ss1615747012="1" href="https://github.com/mweststrate/immer" rel="external nofollow">Immer</a> على شعبية <a data-ss1615747012="1" href="https://github.com/facebook/immutable-js/" rel="external nofollow">immutable.js</a> في السنة الماضية. فهي تؤمن نفس الوظائف لكن ضمن حزمة أسهل استخدامًا نوعًا ما.
</p>

<p>
	تؤمن المكتبة <a data-ss1615747012="1" href="https://redux-saga.js.org/" rel="external nofollow">Redux-saga</a> طرقًا بديلة لإنشاء أفعال غير متزامنة للمكتبة <a data-ss1615747012="1" href="https://fullstackopen.com/en/part6/communicating_with_server_in_a_redux_application#asynchronous-actions-and-redux-thunk" rel="external nofollow">redux thunk</a> التي تعرفنا عليها في <a data-ss1615747012="1" href="https://academy.hsoub.com/programming/javascript/react/%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D9%8A%D8%A9-flux-%D9%88%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D8%A9-redux-%D9%84%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D9%84%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-react-r1162/" rel="">القسم 6</a>.
</p>

<p>
	إن جمع البيانات التحليلية للتفاعل بين الصفحة في تطبيقات الصفحة الواحدة وبين المستخدمين سيحمل <a data-ss1615747012="1" href="https://developers.google.com/analytics/devguides/collection/analyticsjs/single-page-applications" rel="external nofollow">قدرًا من التحدي</a>. ستقدم لك المكتبة <a data-ss1615747012="1" href="https://github.com/react-ga/react-ga" rel="external nofollow">React Google Analytics</a> حلًا لتطبيقات الويب التي تُحمِّل الصفحة بأكملها.
</p>

<p>
	يمكنك أن تستفيد من خبرتك في React عند تطوير تطبيقات الهواتف النقالة باستخدام مكتبة Facebook المشهورة جدًا <a data-ss1615747012="1" href="https://facebook.github.io/react-native/" rel="external nofollow">React Native</a>.
</p>

<p>
	تتقلب الأحوال كثيرًا في مجتمع تطوير JavaScript فيما يتعلق بإدارة تجميع وحزم الملفات المشاريع. وتتغير معايير الممارسة الأفضل بشكل سريع:
</p>

<ul>
<li>
		2011 <a data-ss1615747012="1" href="https://www.npmjs.com/package/bower" rel="external nofollow">Bower</a>
	</li>
	<li>
		2012 <a data-ss1615747012="1" href="https://www.npmjs.com/package/grunt" rel="external nofollow">Grunt</a>
	</li>
	<li>
		2013-14 <a data-ss1615747012="1" href="https://www.npmjs.com/package/gulp" rel="external nofollow">Gulp</a>
	</li>
	<li>
		2012-14 <a data-ss1615747012="1" href="https://www.npmjs.com/package/browserify" rel="external nofollow">Browserify</a>
	</li>
	<li>
		2015- <a data-ss1615747012="1" href="https://www.npmjs.com/package/webpack" rel="external nofollow">Webpack</a>
	</li>
</ul>
<p>
	فقد البرنامج Hipsters لأهميته بعد أن سيطر Webpack على السوق. ثم بدأ البرنامج <a data-ss1615747012="1" href="https://parceljs.org/" rel="external nofollow">Parcel</a> قبل عدة سنوات بكسب عدة جولات لصالحه في السوق، كأداة أبسط وأسرع من Webpack (فالبرنامج Webpack ليس بسيطًا على الإطلاق). لكن بعد انطلاقته الواعدة، لم يستطع البرنامج المنافسة أكثر، ويبدو أن Webpack سيبقى في رأس القائمة.
</p>

<p>
	يزودك الموقع <a data-ss1615747012="1" href="https://reactpatterns.com" ipsnoembed="false" rel="external nofollow">https://reactpatterns.com</a> بقائمة تضم الممارسات الأفضل عند تطوير التطبيقات باستخدام React. وقد تعرفنا بالفعل على بعضها ضمن مادة المنهاج. وهنالك قائمة مشابهة لها هي <a data-ss1615747012="1" href="https://vasanthk.gitbooks.io/react-bits/" rel="external nofollow">react bits</a>.
</p>

<p>
	سيساعدك <a data-ss1615747012="1" href="https://www.reactiflux.com/" rel="external nofollow">Reactiflux</a> وهو مجتمع محادثات لمطوري React على Discord، في الحصول على الدعم بعد إكمالك للمنهاج. ستجد على سبيل المثال قنوات مستقلة للتحدث عن عدد هائل من المكتبات.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a data-ss1615747012="1" href="https://fullstackopen.com/en/part7/Class_components_Miscellaneousk" rel="external nofollow">Class components, Miscellaneous</a> من سلسلة <a data-ss1615747012="1" href="https://fullstackopen.com/en/" rel="external nofollow">Deep Dive Into Modern Web Development</a>
</p>
]]></description><guid isPermaLink="false">1170</guid><pubDate>Thu, 08 Apr 2021 13:04:01 +0000</pubDate></item></channel></rss>
