بداية سريعة

أهلا بك في مستندات react، هذه الصفحة ستعطيك مقدمة ل ٨٠٪ من مفاهيم React التي يتم استخدامها بشكل روتيني في مشاريع React.

You will learn

  • كيف تقوم بإنشاء ودمج المكوّنات.
  • كيف تضيف تأثيرات على العناصر.
  • كيف تقوم بعرض البيانات.
  • كيف تستخدم الجمل الشرطية وتعرض عناصر القوائم.
  • كيف تتجاوب مع الأحداث المختلفة وتحدث شاشة المستخدم بناءً عليها.
  • كيف تشارك البيانات بين أكثر من مكوّن.

إنشاء ودمج المكوّنات

يتم صناعة تطبيقات React اعتمادا على ما يعرف بـ (المكوّنات - Components). المكوّن هو جزء مستقل من واجهة المستخدم والتي لها المنطق البرمجي والشكل الخاص بها. يمكنك تشكيل المكوّن بالحجم والشكل المناسب فيتراوح من كونه صغيرا ليمثل (زر) أو كبيرا ليمثل صفحة كاملة. المكوّنات ليست إلا دوال JavaScript والتي توفر (ترميزا مرئيا) كقيمة مرجعة من الدالة.

function MyButton() {
return (
<button>أنا زر</button>
);
}

الآن قمت ببناء مكوّن باسم MyButton، يمكنك الآن إدخاله في مكون آخر:

export default function MyApp() {
return (
<div>
<h1>مرحبًا بكم في تطبيقي</h1>
<MyButton />
</div>
);
}

لاحظ كيف أن المكوّن <MyButton /> يبدأ بحرف (كبير) في الإنجليزية (M)، وهي طريقة مستخدمة في React للتمييز ومعرفة أن هذه الدالة تمثل مكوّن. أسماء المكوّنات في React يجب أن تبدأ دائما بحرف كبير، بينما يجب أن تكون وسوم ال html المستخدمة بحرف صغير.

خذ نظرة سريعة على النتيجة:

function MyButton() {
  return (
    <button>
      أنا زر
    </button>
  );
}

export default function MyApp() {
  return (
    <div>
      <h1>مرحبًا بكم في تطبيقي</h1>
      <MyButton />
    </div>
  );
}

عبارة export default تحدد المكوّن الأساسي في الملف. إذا لم يكن لديك معرفة كافية عن طريقة بناء وكتابة الكود في JavaScript، فيمكنك الرجوع للمصادر التالية: MDN javascript.info

بناء الترميز المرئي للواجهة باستخدام JSX

طريقة بناء جمل ترميز الواجهات الذي شاهدته في الأعلى هو ما يسمى بـ JSX. استخدام الـ JSX يعتبر اختياريا، لكن معظم مشاريع React تستخدم الـ JSX لسهولة التعامل معها. كل الأدوات التي ننصح بها لبيئة التطوير تدعم الـ JSX.

تعتبر JSX أكثر صرامة من ال HTML. حيث يجب عليك إغلاق العلامات مثل <br />. وكذلك فإن المكوّن الذي تقوم بإنشائه لا يمكن أن يرجع لك مجموعة من علامات ال JSX، بل يجب عليك إحاطتهم بأبٍ مشترك مثل: <div>...</div> أو حتى غطاء <>...</> الفارغ.

function AboutPage() {
return (
<>
<h1>عنوان</h1>
<p>مرحبًا جميعًا.<br />كيف حالكم?</p>
</>
);
}

إذا كان لديك الكثير من الأكواد المكتوبة بال HTML والتي ترغب في تحويلها إلى الـ JSX، فيمكنك استخدام تحويل الـ HTML لـ JSX أون لاين.

إضافة أنماط تصميمية

في React يمكنك تحديد تصنيف باستخدام className. حيث أنها تعمل تماما كما تعمل خاصية class في الـ HTML:

<img className="avatar" />

و بعد ذلك يمكنك كتابة قواعد الـ CSS لهذا التصنيف في ملف CSS منفصل:

/* In your CSS */
.avatar {
border-radius: 50%;
}

React لا تحدد لك كيف يجب عليك إضافة ملفات الـ CSS لملف الـ HTML. ببساطة يمكنك إضافة وسم <link> لملف الـ html الخاص بك. إذا كنت تستخدم أداة بناء معينة أو إطار عمل فاستفسر عن طريقة إضافة ملفات الـ CSS لمشروعك من خلال المستندات الخاصة بالأداة.

عرض البيانات

تمكنك الـ JSX من إدخال ترميز الواجهة بداخل JavaScript، ويمكنك من خلال الأقواس المعقوفة “الخروج من ترميز الواجهة” والعودة مرة أخرى إلى سياق JavaScript حتى تتمكن من تضمين متغير من أكوادك وعرضه للمستخدم، فمثلا الكود التالي سيُظهر user.name للمستخدم على الشاشة:

return (
<h1>
{user.name}
</h1>
);

يمكنك كذلك “الخروج إلى سياق JavaScript” من داخل خواص الـ JSX، لكن يجب عليك حينها استخدام الأقواس المعقوفة بدلا من علامات التنصيص. فمثلا className="avatar" تقوم بتمرير "avatar" على أنه التصنيف المستخدم في الـ CSS، ولكن src={user.imageUrl} تقوم بقراءة قيمة متغير JavaScript التالي: user.imageUrl ومن ثم تقوم بتمرير قيمته لتمثل خاصية الـ src:

return (
<img
className="avatar"
src={user.imageUrl}
/>
);

يمكنك كذلك وضع تعبيرات برمجية أكثر تعقيدا بداخل أقواس الـ JSX المعقوفة، مثلا دمج النصوص:

const user = {
  name: 'هايدي لامار',
  imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
  imageSize: 90,
};

export default function Profile() {
  return (
    <>
      <h1>{user.name}</h1>
      <img
        className="avatar"
        src={user.imageUrl}
        alt={'Photo of ' + user.name}
        style={{
          width: user.imageSize,
          height: user.imageSize
        }}
      />
    </>
  );
}

في المثال الموضح أعلاه style={{}} لا تمثل بناءً خاصا، بل هو كائن JavaScript المعروف {} والمعرف بداخل الأقواس المعقوفة الخاصة بال JSX بداخل style={ }. يمكنك استخدام خاصية style عندما يكون التصميم لديك معتمدا على متغير JavaScript.

التصيير الشرطي

في React، لا يوجد طريقة خاصة لكتابة العبارات الشرطية، وإنما يتم استخدام نفس الطرق المستخدمة في كتابة أكواد JavaScript الاعتيادية، فعلى سبيل المثال يمكنك استخدام عبارة if لإظهار الـ JSX بشكل شرطي:

let content;
if (isLoggedIn) {
content = <AdminPanel />;
} else {
content = <LoginForm />;
}
return (
<div>
{content}
</div>
);

و إذا كنت تفضل استخدام كود أكثر اختصارا فيمكنك استخدام معامل ? الشرطي وتتميز بإمكانية استخدامها في داخل ال JSX على النقيض من if العادية.

<div>
{isLoggedIn ? (
<AdminPanel />
) : (
<LoginForm />
)}
</div>

و في حالة عدم احتياجك لجزئية else فيمكنك استخدام عبارة && المنطقية:

<div>
{isLoggedIn && <AdminPanel />}
</div>

كل الطرق المذكورة في الأعلى يمكن كذلك استخدامها لتحديد قيم الخواص بشكل شرطي. إذا كان هذا النوع من جمل JavaScript غير مألوفا لديك فيمكنك دائما استخدام عبارة if...else الاعتيادية.

تصيير القوائم

ستحتاج إلى الاعتماد على ميزات Javascript مثل for loop وكذلك دالة map() الخاصة بالمصفوفات لإظهار القوائم على المكوّنات.

على سبيل المثال، لنفترض أن لديك مصفوفة لمجموعة من المنتجات كالتالي:

const products = [
{ title: 'ملفوف', id: 1 },
{ title: 'خضروات', id: 2 },
{ title: 'تفاح', id: 3 },
];

قم باستخدام دالة map() بداخل المكوّن الخاص بك لتحويل مصفوفة المنتجات إلى مصفوفة من عناصر الـ <li>:

const listItems = products.map(product =>
<li key={product.id}>
{product.title}
</li>
);

return (
<ul>{listItems}</ul>
);

لاحظ أن كل <li> لديه خاصية باسم key. كل عنصر في أي قائمة يجب أن يتم تمرير رقم أو سلسلة نصية إليه لتميّزه عن كل العناصر الأخرى الموجودة في نفس القائمة. هذه الخاصية عادة ما يتم تعبئتها من صلب البيانات الخاصة بمشروعك مثل الـ ID الخاص بقاعدة البيانات. هذا المفتاح (key) يتم استخدامه من React ولمعرفة ما الذي حدث تحديدا في حال قمت لاحقا بإضافة، حذف أو إعادة ترتيب لعناصر القائمة.

const products = [
  { title: 'ملفوف', isFruit: false, id: 1 },
  { title: 'خضروات', isFruit: false, id: 2 },
  { title: 'تفاح', isFruit: true, id: 3 },
];

export default function ShoppingList() {
  const listItems = products.map(product =>
    <li
      key={product.id}
      style={{
        color: product.isFruit ? 'magenta' : 'darkgreen'
      }}
    >
      {product.title}
    </li>
  );

  return (
    <ul>{listItems}</ul>
  );
}

الاستجابة للأحداث

يمكنك الاستجابة للأحداث عن طريق تعريف (معالج الحدث) أو الـ event handler بداخل المكوّن الخاص بك:

function MyButton() {
function handleClick() {
alert('لقد ضغطت هنا!');
}

return (
<button onClick={handleClick}>
اضغط هنا
</button>
);
}

لاحظ أن onClick={handleClick} لا تحتوي على أقواس الاستدعاء في النهاية! لا تقم باستدعاء معالج الحدث، يكفيك أن تقوم بتمرير اسمها فقط. حيث أن React ستقوم باستدعاء دالة معالجة الحدث عندما يقوم المستخدم بالضغط على الزر.

تحديث الشاشة

غالبا ستحتاج أن “يتذكر” المكوّن المعلومات ويعرضها على الشاشة. فمثلا، قد تحتاج إلى عرض عدد المرات التي تم فيها الضغط على زر ما. لتنفيذ ذلك قم بإضافة ما يعرف ب حالة أو الـ state الخاصة بالمكون:

في البداية، قم باستيراد useState من React:

import { useState } from 'react';

والآن يمكنك تعريف متغير الحالة أو الـ state variable بداخل المكوّن:

function MyButton() {
const [count, setCount] = useState(0);
// ...

ستحصل على شيئين من دالة useState: الحالة الحالية (count)، وكذلك الدالة التي تمكنك من تحديث القيمة (setCount). يمكنك تسميتهما ما تشاء، ولكن المتعارف عليه أن يتم تسميتها بالشكل التالي: [something, setSomething].

في المرة الأولى التي يظهر فيها الزر ستكون قيمة coount تساوي 0 وذلك لأنك قمت بتمرير 0 كبارامتر لدالة useState(). عندما تريد تغيير الحالة، قم باستدعاء setCount() وتمرير القيمة الجديدة لها. وبالتالي فإن الضغط على هذا الزر سيزيد العداد count.

function MyButton() {
const [count, setCount] = useState(0);

function handleClick() {
setCount(count + 1);
}

return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}

الآن ستقوم React باستدعاء المكوّن الخاص بك مجددا. في هذه المرة قيمة count ستكون 1، ومن ثم ستكون 2، وهكذا. إذا قمت تصيير المكوّن عدة مرات فإن كل مرة ستكون لها الحالة المستقلة الخاصة بها. قم بتجربة الضغط على كل زر على حدة:

import { useState } from 'react';

export default function MyApp() {
  return (
    <div>
      <h1>العدادات التي تتغير مستقلة</h1>
      <MyButton />
      <MyButton />
    </div>
  );
}

function MyButton() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      Clicked {count} times
    </button>
  );
}

لاحظ كيف أن كل زر قادر على “تذكر” حالة الـ count الخاصة به دون أي تعارض مع حالة الزر الآخر.

استخدام الخطاطيف

الدوال التي تبدأ بـ use تسمى الخطاطيف أو Hooks. تعتبر useState أحد الخطاطيف المجهّزة والتي توفرها React. وبإمكانك العثور على قائمة الخطاطيف المختلفة التي توفرها React في API مرجع. ، يمكنك كذلك كتابة الخطاطيف الخاصة بك عن طريق الجمع بين الخطاطيف الجاهزة والموفّرة من React.

الخطاطيف أكثر صرامة من الدوال الأخرى. يمكنك استدعاء الخطاف في مقدمة المكوّن (أو في مقدمة أي خطاف آخر). إذا أردت استخدام useState في عبارة شرطية أو في حملة تكرار فقم ببناء مكوّن مستقل ومن ثم استخدامها هناك.

مشاركة البيانات بين المكوّنات

في المثال السابق، كل MyButton كان لديه حالة الـ count المستقلة الخاصة به، وعندما يتم الضغط على أي منهما حينها يتم تعديل الـ count الخاصة بذلك الزر فقط:

رسم تخطيطي يوضح شجرة من ثلاثة مكونات ، أحد الوالدين يسمى MyApp وطفلان يسمى MyButton.  يحتوي كلا المكونين MyButton على عدد بقيمة صفر.
رسم تخطيطي يوضح شجرة من ثلاثة مكونات ، أحد الوالدين يسمى MyApp وطفلان يسمى MyButton.  يحتوي كلا المكونين MyButton على عدد بقيمة صفر.

مبدئيا، حالة count الخاصة بكل زر تساوي 0

نفس الرسم التخطيطي كالسابق ، مع تمييز أول مكون MyButton فرعي يشير إلى نقرة مع زيادة قيمة العد إلى واحد.  لا يزال مكون MyButton الثاني يحتوي على القيمة صفر.
نفس الرسم التخطيطي كالسابق ، مع تمييز أول مكون MyButton فرعي يشير إلى نقرة مع زيادة قيمة العد إلى واحد.  لا يزال مكون MyButton الثاني يحتوي على القيمة صفر.

مكوّن MyButton الأول سيقوم بتحديث حالة الـ count الخاصة به إلى 1

و بالرغم من ذلك، فإنك غالبا ستحتاج أن تشارك المكوّنات البيانات وأن يتم تحديثها معا و لجعل كلا المكونين MyButton يقومان بعرض نفس الـ count ويتم تحديثهما معا فسنحتاج إلى نقل الحالة من كل زر بشكل مستقل “إلى الأعلى” لأقرب مكوّن يحوي كلا المكوّنين. في هذا المثال MyApp يمثل هذا هذا المكون الأب الذي يحوي كل المكونين:

رسم تخطيطي يوضح شجرة من ثلاثة مكونات ، أحد الوالدين يسمى MyApp وطفلان يسمى MyButton.  يحتوي MyApp على قيمة عد صفرية يتم تمريرها إلى كل من مكوني MyButton ، والتي تعرض أيضًا القيمة صفر.
رسم تخطيطي يوضح شجرة من ثلاثة مكونات ، أحد الوالدين يسمى MyApp وطفلان يسمى MyButton.  يحتوي MyApp على قيمة عد صفرية يتم تمريرها إلى كل من مكوني MyButton ، والتي تعرض أيضًا القيمة صفر.

في البداية حالة الـ count الخاصة بال MyApp تساوي 0، ويتم تمريرها للأسفل لكلا الابنين.

نفس الرسم التخطيطي كالسابق ، مع إبراز عدد مكون MyApp الأصلي للإشارة إلى نقرة مع زيادة القيمة إلى واحد.  يتم أيضًا تمييز التدفق إلى كل من مكونات MyButton الأطفال ، ويتم تعيين قيمة العد في كل طفل على واحد يشير إلى أن القيمة قد تم تمريرها لأسفل.
نفس الرسم التخطيطي كالسابق ، مع إبراز عدد مكون MyApp الأصلي للإشارة إلى نقرة مع زيادة القيمة إلى واحد.  يتم أيضًا تمييز التدفق إلى كل من مكونات MyButton الأطفال ، ويتم تعيين قيمة العد في كل طفل على واحد يشير إلى أن القيمة قد تم تمريرها لأسفل.

عند الضغط على أي من الزرين سيقوم ال MyApp بتحديث حالة الـ count الخاصة به إلى 1 ومن ثم يمرر القيمة إلى الأسفل لكلا الإبنين

و الآن عند الضغط على أي من الزرين، فإن الـ count في MyApp ستتغير، وبالتالي سيغير ذلك بدوره كلا العدادين المتواجدين في MyButton. يمكنك تمثيل ذلك بالكود كالتالي:

في البداية قم بنقل الحالة للأعلى من MyButton إلى MyApp:

export default function MyApp() {
const [count, setCount] = useState(0);

function handleClick() {
setCount(count + 1);
}

return (
<div>
<h1>العدادات التي تتغير مستقلة</h1>
<MyButton />
<MyButton />
</div>
);
}

function MyButton() {
// ... نقلت كودا من هنا ...
}

و من ثم قم بتمرير الحالة للأسفل من MyApp لكلا مكوّني الـ MyButton بالإضافة إلى دالة معالجة الضغط على الزر (click handler). بإمكانك إرسال معلومات إلى MyButton باستخدام أقواس الـ JSX المعقوفة، تماما كما فعلت سابقا في الأوسمة الجاهزة مثل وسم <img>:

export default function MyApp() {
const [count, setCount] = useState(0);

function handleClick() {
setCount(count + 1);
}

return (
<div>
<h1>العدادات التي تتغير معا</h1>
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}

هذه المعلومات التي تقوم بتمريرها للأسفل تسمى props أو خواص المكوّن. والآن مكون MyApp يحتوي على حالة الـ count بالإضافة لمعالج الحدث باسم handleClick، ويقوم بإرسالهما للأسفل لكلا الزرين.

أخيرا غيًر كود MyButton ليقرأ خواص المكوّن التي قمت بتمريرها إليه من مكوّن الأب:

function MyButton({ count, onClick }) {
return (
<button onClick={onClick}>
ضغطت {count} مرات
</button>
);
}

عندما تقوم بالضغط على الزر سيتم تشغيل معالج الحدث الذي يحمل اسم onClick. خاصية معالجة الحدث المرسلة لكلا الزرين والتي تحمل اسم onClick في كليهما هي في حقيقة الأمر تمثل دالة الـ handleClick المعرّفة بداخل الـ MyApp، وبالتالي سيتم تنفيذ الكود المعرّف بداخلها. ذلك الكود سيقوم باستدعاء setCount(count + 1)، والذي بدوره سيزيد حالة الـ count. ومن ثم فإن القيمة الجديدة للـ count سيتم تمريرها لكلي الزرين وبالتالي فإن كليهما سيُظهران نفس القيمة. هذا ما يعرف بـ “نقل الحالة للأعلى”. بنقلك للحالة للأعلى تكون قد جعلت الحالة مشتركة بين المكوّنات.

import { useState } from 'react';

export default function MyApp() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
      <h1>العدادات التي تتغير معًا</h1>
      <MyButton count={count} onClick={handleClick} />
      <MyButton count={count} onClick={handleClick} />
    </div>
  );
}

function MyButton({ count, onClick }) {
  return (
    <button onClick={onClick}>
      ضغطت {count} مرات
    </button>
  );
}

الخطوات القادمة

والآن أنت تعرف أساسيات بناء كود React! اطّلع على هذا الدرس التطبيقي لتفعيل هذه الأساسيات بشكل عملي وبناء مشروعك المصغّر الأوّل باستخدام React.