المكونات والخاصيات في React

من موسوعة حسوب
مراجعة 11:34، 31 أكتوبر 2020 بواسطة محمد-بغات (نقاش | مساهمات) (تحديث)
(فرق) → مراجعة أقدم | المراجعة الحالية (فرق) | مراجعة أحدث ← (فرق)

تتيح لنا المُكوِّنات (Components) تقسيم واجهة المستخدم إلى قطع مُستقِلَّة قابلة لإعادة الاستخدام، والتفكير بكل قطعة على انفراد. سنتحدّث في هذه الصفحة عن مُقدّمة إلى مفهوم المُكوِّنات، بإمكانك أن تجد مرجعًا مُفصَّلًا حول واجهة برمجة التطبيق (API) الخاصّة بالمُكوِّنات من هنا.

تُشبِه المُكوِّنات من الناحية النظريّة دوال JavaScript، فهي تقبل مُدخَلات المستخدم (والتي تُدعى props اختصارًا للكلمة properties وتعني الخاصيّات) وتُعيد عناصر React تصف ما الذي ينبغي عرضه على الشّاشة.

مكونات الأصناف والدوال

إنّ أبسط طريقة لتعريف مُكوِّن هي كتابة دالة JavaScript:

function Welcome(props) {
  return <h1>أهلًا {props.name}</h1>;
}

تُعدّ هذه الدالة مُكوِّنًا صالحًا في React لأنّها تقبل وسيطًا واحدًا من خاصيّات الكائن props (اختصارًا للكلمة properties وتعني الخاصيّات) مع بياناته وتُعيد عنصر React. ندعو مثل هذه المُكوِّنات بالمُكوِّنات الداليّة (functional) لأنّها عبارة عن دوال JavaScript. بإمكانك أيضًا أن تستخدم الأصناف لتعريف المُكوِّنات كما يلي:

class Welcome extends React.Component {
  render() {
    return <h1>أهلًا {this.props.name}</h1>;
  }
}

إنّ المُكوِّنين السابقين مُتكافِئان من وجهة نظر React.

تمتلك الأصناف بعض الميّزات الإضافيّة التي سنتحدّث عنها في قسم حالة ودورة حياة المُكوِّنات، وحتى ذلك الوقت سنستخدم المُكوِّنات الداليّة لبساطتها.

تصيير المكوّنات (Rendering)

لم نصادف حتى الآن إلّا عناصر React تُمثِّل عناصر DOM المُعتادة:

const element = <div />;

ولكن يُمكِن للعناصر أن تُمثِّل مُكوِّنات مُعرَّفة من قبل المستخدم:

const element = <Welcome name="سارة" />;

عندما تجد React عنصرًا يُمثِّل مُكوِّنًا مُعرَّفًا من قبل المستخدم، فستُمرِّر خاصيّات JSX إليه على شكل كائن وحيد، ندعو هذا الكائن props. على سبيل المثال تعرض هذه الشيفرة عبارة "أهلًا سارة" في الصّفحة:

function Welcome(props) {
  return <h1>أهلًا {props.name}</h1>;
}

const element = <Welcome name="سارة" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

جرِّب هذا المثال على موقع CodePen.

تلخيص ما حدث في هذا المثال:

  1. نستدعي التّابع ReactDOM.render()‎ مع العنصر </ "سارة"=Welcome name>.
  2. تستدعي React المُكوِّن Welcome مع تمرير {'سارة' :name} كخاصيّة props.
  3. يُعيد العنصر Welcome العنصر ‏<h1/>أهلًا سارة<h1>‏ كنتيجة له.
  4. تُحدِّث React DOM بكفاءة DOM ليُطابِق <h1/>أهلًا سارة<h1>.

ملاحظة: يجب أن تبدأ أسماء المُكوِّنات دومًا بأحرف كبيرة.

تُعامِل React المُكوِّنات التي تبدأ بأحرف صغيرة كعناصر DOM، على سبيل المثال يُمثِّل ‎<div>‎ عنصر HTML الذي يُدعى div، بينما تُمثِّل ‎<Welcome />‎ مُكوِّنًا في React وتتطلَّب أن يكون تعريف هذا المُكوِّن موجودًا ضمن المجال المُحدَّد.

بإمكانك قراءة المزيد عن المنطق الكامن وراء هذه الاتفاقيّة من هنا.

تركيب المكونات

يُمكِن للمُكوِّنات أن تشير إلى مُكوِّنات أخرى في ناتجها، يسمح لنا هذا باستخدام نفس المُكوِّن المُجرَّد لأي درجة من التفصيل، زر (button)، أو حقل إدخال (form)، أو مربّع حوار (dialog)، أو شاشة (screen)، ففي React يُعبَّر عنها جميعها بالمُكوِّنات.

على سبيل المثال يُمكننا إنشاء مُكوِّن اسمه App يعرض في ناتجه المُكوِّن Welcome عدّة مرّات:

function Welcome(props) {
  return <h1>أهلًا {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Welcome name="سارة" />
      <Welcome name="محمد" />
      <Welcome name="عبد الله" />
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

جرِّب المثال على موقع CodePen.

تحتوي تطبيقات React الجديدة عادةً على المُكوِّن App في المستوى الأعلى وتنحدر عنه باقي المُكوِّنات، ولكن إن كنت تدمج React مع تطبيق موجود مُسبقًا فقد تبدأ من المستوى السفلي بمُكوِّن صغير مثل الزر Button وتصعد تدريجيًّا حتى المستوى الأعلى في هيكليّة التطبيق.

استخراج المكونات

لا تتردد بتقسيم المُكوِّنات إلى مُكوِّنات أصغر. على سبيل المثال انظر إلى مُكوِّن التعليقات Comment التالي:

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <img className="Avatar"
          src={props.author.avatarUrl}
          alt={props.author.name}
        />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

جرِّب المثال على موقع CodePen.

يقبل هذا المُكوِّن الكائن author، والسلسلة النصيّة text، والتاريخ date كخاصيات props له، ويُمثِّل تعليقًا على مواقع التواصل الاجتماعي.

من الصعب تغيير هذا المُكوِّن بسبب هذه التداخلات، ومن الصعب أيضًا إعادة استخدام أجزاء منه، فلنحاول استخراج بعض المُكوِّنات منه.

سنستخرج في البداية مُكوِّن الصورة الرمزيّة Avatar:

function Avatar(props) {
  return (
    <img className="Avatar"
      src={props.user.avatarUrl}
      alt={props.user.name}
    />

  );
}

لا يحتاج المُكوِّن Avatar إلى معرفة أنّه مُستخدَم في المُكوِّن Comment، ولذلك أعطينا خاصيّاته (props) اسمًا أكثر عموميّةً وهو user بدلًا من author. حيث نوصي بتسمية الخاصيّات props من وجهة نظر المُكوِّن نفسه وليس في السياق الذي تُستخدَم فيه. بإمكاننا الآن تبسيط المُكوِّن Comment قليلًا:

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <Avatar user={props.author} />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

سنستخرج الآن مُكوِّن معلومات المستخدم UserInfo والذي يعرض المُكوِّن Avatar بجانب اسم المستخدم:

function UserInfo(props) {
  return (
    <div className="UserInfo">
      <Avatar user={props.user} />
      <div className="UserInfo-name">
        {props.user.name}
      </div>
    </div>
  );
}

يُتيح لنا هذا تبسيط المُكوِّن Comment أكثر:

function Comment(props) {
  return (
    <div className="Comment">
      <UserInfo user={props.author} />
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

جرِّب المثال على موقع CodePen.

يبدو استخراج المُكوِّنات في البداية عملًا مجهدًا، ولكن سنرى الفائدة الكبيرة لامتلاك عدّة مُكوِّنات قابلة لإعادة الاستخدام عند بناء تطبيقات كبيرة. القاعدة هنا هي: إن استخدمنا أجزاء واجهة المستخدم عدّة مرّات (مثل الزر Button، و اللوحة Panel، والصورة الرمزيّة Avatar)، أو كانت هذه الأجزاء مُعقّدة بحد ذاتها (مثل مُكوِّن التطبيق App، و FeedStory، والتعليق Comment)، فهي مُرشَّحة بشكل كبير لأن نجعلها مُكوِّنات قابلة لإعادة الاستخدام.

الخاصيات props قابلة للقراءة فقط

لا يجب تعديل خاصيّات المُكوِّنات، سواءً صرّحنا عنها كدالة أو كصنف، فلنأخذ هذا المثال عن دالة الجمع sum:

function sum(a, b) {
  return a + b;
}

تُدعى هذه الدوال بالدوال النقيّة (Pure Functions) لأنّها لا تحاول تغيير مُدخلاتها وتُعيد دومًا نفس النتيجة لنفس المُدخلات. وعلى العكس من ذلك نجد أنّ هذه الدالة غير نقية لأنّها تُغيِّر مُدخلاتها:

function withdraw(account, amount) {
  account.total -= amount;
}

إنّ React مرنة جدًّا ولكن لديها قاعدة واحدة صارمة وهي:

يجب أن تسلك مُكوِّنات React سلوك الدوال النقيّة مع احترامها لخاصيّاتها (props).

تتغيّر واجهات المستخدم الخاصّة بالتطبيق مع الزمن بالطبع، سنتحدّث في قسم حالة ودورة حياة المُكوِّنات حول مفهوم جديد وهو الحالة (state)، حيث تسمح الحالة لمُكوِّنات React بتغيير ناتجها مع مرور الزمن استجابةً لتفاعل المستخدم، والردود القادمة من الشبكة، وأي شيء آخر، بدون خرق هذه القاعدة.

انظر أيضًا

مصادر