قواعد استعمال الـ Hooks

الخطافات هي إضافة جديدة إلى الإصدار 16.8 في React، إذ تسمح لك باستعمال ميزة الحالة وميزات React الأخرى دون كتابة أي صنف:

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

استدعي الخطافات في المستوى الأعلى فقط

لا تستدعي الخطافات داخل حلقات التكرار، أو التعابير الشرطية، أو الدوال المتشعبة. بدلًا من ذلك، استعمل الخطافات دومًا في المستوى الأعلى (top level) من دالة React. بتطبيق هذه القاعدة، تتأكد من أن الخطافات تُستدَعى بالترتيب نفسه في كل مرة يصيَّر فيها مكونٌ. هذا يسمح لـ React بحفظ حالة الخطافات بين الاستدعاءات useState و useEffect المتعددة بشكل صحيح. (إن شدَّك فضولك للتعلم أكثر، سنشرح هذا الأمر بالتفصيل في الأسفل.)

استدعي الخطافات من دوال React فقط

لا تستدعي الخطافات من دوال JavaScript العادية. بدلًا من ذلك، يمكنك:

  • ✅ استدعاء الخطافات من مكونات دالة React.
  • ✅ استدعاء الخطافات من خطافات مخصصة (سنتعرف عليهم في الصفحة التالية).

باتباع هذه القاعدة، تتأكد من أنَّ الشيفرة ذات الحالة (stateful logic) في مكونٍ ما مرئيةٌ بوضوح من شيفرتها المصدرية.

الإضافة ESLint

أطلقنا إضافةً لتصحيح الأخطاء تدعى eslint-plugin-react-hooks تجبر على تطبيق هاتين القاعدتين. يمكنك إضافة هذه الإضافة إلى مشروعك إن أحببت تجريبها:

npm install eslint-plugin-react-hooks --save-dev
// الخاص بك ESLint ضبط الإضافة
{
  "plugins": [
    // ...
    "react-hooks"
  ],
  "rules": {
    // ...
    "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
    "react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
  }
}

في المستقبل، ننوي تضمين هذه الإضافة افتراضيًّا عند بناء تطبيقات جديدة باستعمال الأمر Create React App أو أية مجموعة أدوات أخرى (toolkits).

يمكنك تخطي بقية هذه الصفحة إلى الصفحة التالية: بناء خطافات خاصة بك الآن. إذ سنكمل شرح سبب وجود هاتين القاعدتين.

الشرح

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

function Form() {
  // 1. نفسه name استعمال متغير الحالة 
  const [name, setName] = useState('Mary');

  // 2. استعمال تأثير من أجل استمرار النموذج
  useEffect(function persistForm() {
    localStorage.setItem('formData', name);
  });

  // 3. surname استعمال متغير الحالة
  const [surname, setSurname] = useState('Poppins');

  // 4. استعمال تأثير من أجل تحديث العنوان
  useEffect(function updateTitle() {
    document.title = name + ' ' + surname;
  });

  // ...
}

إذًا، كيف تعرف React أية حالة توافق أي استدعاء للخطاف useState؟ الجواب هو أنَّ React تعتمد على الترتيب الذي استدعيت الخطافات به. مثالنا السابق يعمل بشكل صحيح لأنَّ ترتيب استدعاء الخطافات هو نفسه في كل تصيير:

// ------------
// أول تصيير
// ------------
useState('Mary')           // 1. 'Mary' إلى name ضبط متغير الحالة
useEffect(persistForm)     // 2. persistForm إضافى تأثير من أجل استمرار النموذج
useState('Poppins')        // 3. 'Poppins' إلى surname ضبط متغير الحالة
useEffect(updateTitle)     // 4. إضافة تأثير من أجل تحديث العنوان

// -------------
// ثاني تصيير
// -------------
useState('Mary')           // 1. (يتجاهل الوسيط) name قراءة متغير الحالة
useEffect(persistForm)     // 2. استبدال التأثير من أجل استمرار النموذج
useState('Poppins')        // 3. (يتجاهل الوسيط) surname قراءة متغير الحالة
useEffect(updateTitle)     // 4. استبدال التأثير من أجل تحديث العنوان

// ...

طالمًا أنَّ ترتيب استدعاءات الخطاف هو نفسه بين عمليات التصيير، تستطيع React من ربط بعض الحالات المحلية مع بعضها بعضًا. ولكن، ماذا يحصل إن وضعنا استدعاء خطاف (التأثير persistForm مثلًا) داخل تعبير شرطي؟

  // 🔴 نكسر الآن القاعدة الأولى عبر استعمال خطاف داخل تعبير شرطي

  if (name !== '') {
    useEffect(function persistForm() {
      localStorage.setItem('formData', name);
    });
  }

الشرط name !== '' هو true في عملية التصيير الأولى، لذا يُنفَّذ هذا الخطاف. مع ذلك، في عملية التصيير التالية، قد يمسح المستخدم النموذج مما يؤدي إلى تغيِّر قيمة الشرط إلى false. الآن وبعد أن تخطينا هذا الخطاف أثناء عملية التصيير، أصبح ترتيب استدعاءات الخطاف مختلفًا:

useState('Mary')           // 1. (يتجاهل الوسيط) name قراءة متغير الحالة
// useEffect(persistForm)  // 🔴 يُتخطَى هذا الخطاف
useState('Poppins')        // 🔴 2 (يجب أن تكون 3) surname فشل قراءة متغير الحالة
useEffect(updateTitle)     // 🔴 3 (فشل استبدال التأثير (يجب أن تكون 4

لن تعرف React ما الذي ستعيده من أجل استدعاء الخطاف useState الثاني. تتوقع React أن استدعاء الخطاف الثاني في هذا المكون يقابل التأثير persistForm مثل عملية التصيير السابقة، ولكن الحالة الآن لم تعد مشابه لها. بدءًا من تلك النقطة، كل استدعاء خطاف لاحق بعد ذلك الخطاف الذي جرى تخطيه سينزاح مرةً واحدةً مما يؤدي إلى حصول أخطاء.

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

  useEffect(function persistForm() {
    // 👍 لم نخرق القاعدة الأولى
    if (name !== '') {
      localStorage.setItem('formData', name);
    }
  });

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

الخطوات التالية

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