كيفية استخدام المكونات الوظيفية في React: دليل شامل للمطورين

دقائق القراءة: 5

مقدمة إلى المكونات الوظيفية في React

هل تساءلت يوماً عن أسهل طريقة لإنشاء مكون (Component) في مكتبة React؟ الإجابة تكمن في بساطة تعريف دالة JavaScript تعيد بناءً يشبه صيغة HTML. لقد أحدثت المكونات الوظيفية (Functional Components) ثورة في طريقة بناء واجهات المستخدم، مقدمةً أسلوباً أكثر نظافة وقابلية للتوسع مقارنة بالمكونات المبنية على الفئات (Class Components).

بناء أول مكون وظيفي

لنبدأ بمثال بسيط يوضح كيفية إنشاء مكون وظيفي أساسي. تخيل أننا نريد عرض عداد بسيط يأخذ رقماً ويعرضه على الشاشة:


import React from 'react';

function Counter({ n }) {
  return (
    <div>
      {n}
    </div>
  );
}

export default Counter;

في هذا المثال، Counter هي دالة بسيطة تتلقى خاصية (Prop) واحدة باسم n وتقوم بتحويلها إلى عنصر HTML من نوع div. إذا أمعنت النظر، ستلاحظ أن Counter هي دالة نقية (Pure Function).

مفهوم الدوال النقية والآثار الجانبية

الدالة النقية هي دالة تعيد نفس النتيجة دائماً لنفس المدخلات، ولا تُحدث أي آثار جانبية (Side-effects). الآثار الجانبية هي أي تعديل على البيئة الخارجية للدالة، أو قراءة أي معلومات من البيئة الخارجية يمكن أن تتغير بمرور الوقت. المكونات الوظيفية في جوهرها تسعى لتكون دوالاً نقية قدر الإمكان، مما يسهل اختبارها وفهمها.

تمرير الخصائص (Props) للمكونات

ربما لاحظت استخدام صيغة التفكيك (Destructuring Assignment) في قائمة المعاملات ({ n }). هذا لأن المكونات في React تتلقى كمدخل كائناً واحداً يُسمى props، والذي يحتوي على جميع الخصائص المرسلة إليها. يمكن تعيين المعامل n من أي مكون آخر بالشكل التالي:


<Counter n={1} />

يمكن تخيل هذه الصيغة كاستدعاء دالة Counter({n: 1})، أليس كذلك؟ هذا التجريد يجعل تمرير البيانات بين المكونات سلساً وواضحاً.

فهم الحالة (State) في المكونات الوظيفية: التحديات والحلول

السؤال الذي يطرح نفسه الآن هو: هل يمكن للمكونات الوظيفية أن تمتلك حالة (State)؟ إذا أردنا تخزين وتغيير قيمة عداد داخل المكون، فكيف نفعل ذلك؟

لماذا لا تعمل المتغيرات العادية كحالة؟

لنفترض أننا حاولنا ببساطة تعريف متغير يحمل رقماً داخل المكون:


import React from 'react';

function Counter() {
  let n = 0; // تعريف متغير عادي
  return (
    <div>
      {n}
    </div>
  );
}

export default Counter;

إذا قمنا بتعريف المتغير n بهذه الطريقة، فإن قيمته ستُعاد إلى 0 في كل مرة يُعاد فيها عرض المكون (re-render)، ولن يتم الاحتفاظ بالحالة بين عمليات العرض المختلفة.

التعامل مع الأحداث وتحديث الواجهة

لنضف الآن دالة تزيد الرقم وتطبعه في وحدة التحكم (console)، ونستخدمها كمعالج لحدث النقر onClick:


import React from 'react';

function Counter() {
  let n = 0;

  function increment() {
    n = n + 1;
    console.log(n); // لطباعة القيمة في الكونسول
  }

  return (
    <div>
      <span>{n}</span>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

export default Counter;

إذا نظرت إلى وحدة التحكم، ستجد أن الرقم يزداد بالفعل مع كل نقرة. ولكن، هذا التغيير لا ينعكس على الشاشة! لماذا؟ لأننا بحاجة ليس فقط لتغيير الرقم، بل أيضاً لإعادة عرض المكون على الشاشة ليعكس القيمة الجديدة. هنا يأتي دور الدوال المساعدة من React Hooks.

إدارة الحالة باستخدام خطاف useState()

تُعرف الدوال المساعدة في React باسم “الخطافات” (Hooks)، وتبدأ جميعها بكلمة use. سنستخدم أحد أهم هذه الخطافات: useState. لإظهار عدد مرات استدعاء دالة Counter فعلياً، سنضيف نص “re-render” للطباعة في وحدة التحكم.


import React, { useState } from 'react';

function Counter() {
  const [n, setN] = useState(0); // استخدام خطاف useState
  console.log('re-render'); // لمعرفة عدد مرات إعادة العرض

  function increment() {
    setN(n + 1); // تحديث الحالة باستخدام setN
    console.log(n); // هذه القيمة قد لا تعكس التحديث الفوري
  }

  return (
    <div>
      <span>{n}</span>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

export default Counter;

آلية عمل useState()

ماذا يفعل useState() بالضبط؟ إنه يعيد زوجاً من القيم: الحالة الحالية (Current State) ودالة لتحديثها (Updater Function). في مثالنا، n هي الحالة الحالية، و setN() هي الدالة التي تقوم بتحديثها. عندما يتم استدعاء setN()، تقوم React بإعادة عرض المكون (re-render) مع القيمة الجديدة للحالة، مما يضمن تحديث الواجهة.

يمكنك التحقق من وحدة التحكم لترى كم مرة يتم عرض نص “re-render”؛ ستلاحظ أنه يظهر في كل مرة تتغير فيها الحالة ويُعاد عرض المكون.

تحديث الحالة بدوال نقية

لا يقتصر تحديث الحالة على تعيين قيمة جديدة مباشرة، بل يمكن أيضاً توفير دالة تعيد القيمة الجديدة. في حالتنا، الدالة التي توفر القيمة الجديدة ستكون increment(). وكما ترى، increment() هي دالة نقية.


import React, { useState } from 'react';

function increment(n) {
  return n + 1;
}

function Counter() {
  const [n, setN] = useState(0);

  return (
    <div>
      <span>{n}</span>
      <button onClick={() => setN(increment)}>Increment</button>
    </div>
  );
}

export default Counter;

مزايا استخدام دالة التحديث

لفهم ما تفعله setN(increment)، يجب أن نعلم أن تمرير دالة تحديث يسمح لك بالوصول إلى قيمة الحالة الحالية داخل المحدث. هذا يعني أن الدالة increment() سيتم استدعاؤها مع قيمة n الحالية للحالة، وتُستخدم لحساب قيمة الحالة الجديدة. هذه الطريقة مفيدة بشكل خاص عندما يعتمد التحديث الجديد على الحالة السابقة، وتساعد في تجنب المشاكل المتعلقة بالتزامن (Stale Closures).

الخلاصة التقنية

لقد استكشفنا في هذا الدليل كيفية بناء المكونات الوظيفية في React، والتي تُعد أسلوباً حديثاً وفعالاً لإنشاء واجهات المستخدم. تعلمنا أن المكونات الوظيفية هي في جوهرها دوال بسيطة تعيد بناءً يشبه HTML. الأهم من ذلك، تعرفنا على خطافات React Hooks، وبالتحديد useState()، الذي يمكّننا من إضافة وإدارة الحالة داخل هذه المكونات الوظيفية بكل سهولة. أحد أبرز الفوائد التي تجلبها المكونات الوظيفية مع Hooks هو التخلص من الحاجة إلى استخدام الكلمة المفتاحية this، والتي غالباً ما كانت تسبب ارتباكاً بسبب تغير سياقها (context). هذا التبسيط يجعل الكود أكثر وضوحاً وأقل عرضة للأخطاء، مما يعزز تجربة المطور ويحسن من قابلية صيانة المشروع.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *