React/hooks overview

من موسوعة حسوب
مراجعة 09:53، 12 فبراير 2019 بواسطة جميل-بيلوني (نقاش | مساهمات) (إنشاء الصفحة)
(فرق) → مراجعة أقدم | المراجعة الحالية (فرق) | مراجعة أحدث ← (فرق)

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

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

شرح أوسع: اقرأ القسم "الحافز لإضافة الخطافات" لمعرفة سبب إضافة الخطافات إلى React.

إذ تحوي مثل هذه الملاحظة الموجودة في نهاية كل قسم على رابط يحوي شرحًا موسَّعًا يمكن الرجوع إليه.

خطاف الحالة

المثال التالي يصيِّر عدادًا، إذ ستزيد القيمة عند الضغط على الزر:

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

في هذه الشيفرة، useState هو خطاف (سنتحدث عن ما الذي يعينه هذا الأمر). نستدعي هذا الخطاف داخل مكون دالة لإضافة بعض الحالات المحلية إليه. ستحافظ React على هذه الحالة بين عمليات إعادة التصيير. يعيد useState زوجًا هو: قيمة الحالة الحالية ودالة تمكنك من تحديثها. يمكنك استدعاء هذه الدالة من معالج حدث أو أي مكان آخر. هي تشبه this.setState في صنف باستثناء أنها لا تدمج الحالة القديمة مع الحالة الجديدة. (سنظهر الفرق بين useState و this.state عبر مثال عملي في صفحة "استعمال خطاف الحالة".)

الوسيط الوحيد الذي يمكنك تمريره إلى useState هو الحالة الأولية. في المثال أعلاه، الحالة الأولية هي 0 لأننا افترضنا أن العداد يجب أن يبدأ العد من الصفر. لاحظ أنه بخلاف this.state، ليس اجباريًّا أن تكون الحالة هنا كائنًا رغم أنَّ تكون كذلك أن أردت أنت ذلك. وسيط الحالة الأولية يستعمل فقط في عملية التصيير الأولى.

التصريح عن متغيرات عديدة للحالة

يمكنك استعمال خطاف الحالة (State Hook) أكثر من مرة مكون واحد بالشكل التالي:

function ExampleWithManyStates() {
  // Declare multiple state variables!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // ...
}

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

ولكن ما هو الخطاف؟

الخطافات هي دوال تمكنك من "تعليق" (hook into) حالة React وميزات دورة الحياة من مكونات الدالة. لا تعمل الخطافات داخل الأصناف، إذ تمكنك من استعمال React دون الحاجة إلى الأصناف. (لا ننصح بإعادة كتابة المكونات الحالية الخاصة بك بين عشية وضحاها ولكن ننصح ببدء استعمال الخطافات في المكونات الجديدة إن أردت ذلك.)

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

شرح أوسع: يمكنك تعلم المزيد حول خطاف الحالة في توثيق مخصص به هو "استعمال خطاف الحالة".

خطاف التأثير

من المرجح أنك أجريت بعض عمليات جلب البيانات، أو الاشتراكات، أو تغير DOM يدويًا من مكونات React من قبل. نطلق على هذه العمليات بأنها عمليات تملك "تأثيرات جانبية" (side effects، أو "تأثيرات" فقط للاختصار) لأنها تستطيع أن تؤثر على مكونات أخرى ولا يمكن تنفيذها أثناء عملية التصيير.

خطاف التأثير (Effect Hook) - الذي هو useEffect - يضيف القدرة على تنفيذ تأثيرات جانبية من مكون دالة. إنه يخدم الغرض ذاته الذي يفعله componentDidMount، و componentDidUpdate، و componentWillUnmount في أصناف React، ولكن في واجهة برمجية واحدة. (سنجرى عملية موازنة ونظهر الفرق بين useEffect وهذه التوابع مع أمثلة عملية في صفحة "استعمال خطاف التأثير".)

على سبيل المثال، المكون التالي يضبط عنوان الصفحة بعد تحديث React شجرة DOM:

import React, { useState, useEffect } from 'react';

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

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

عند استدعاء useEffect، نخبر بذلك React بتنفيذ الدالة "effect" الخاصة بك بعد تطبيق التغييرات على الشجرة DOM. يُصرَّح عن التأثيرات داخل المكون، لذا يمكنها الوصول إلى خاصياته (props) وحالته (state). افتراضيًّا، تنفذ React التأثيرات بعد كل عملية تصيير بما فيها عملية التصيير الأولى. (سنتحدث بالتفصيل عن هذا السلوك مع موازنته مع سلوك دورات حياة صنف في الصفحة "استعمال خطاف التأثير".) قد تحدِّد التأثيرات اختياريًّا كيفية إجراء عملية "تنظيف" بعد تنفيذها عبر إعادة دالة. على سبيل المثال، يستعمل المكون التالي تأثيرًا للاشتراك بحالة اتصال صديق (friend’s online status) ثم يجري عملية تنظيف عبر إلغاء الاشتراك منها:

import React, { useState, useEffect } from 'react';

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);

    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

في هذا المثال، ستلغي React الاشتراك من ChatAPI عند فصل (unmount) المكون وقبل إعادة تنفيذ التأثير نتيجة عملية التصيير اللاحقة. (إن أردت، هنالك طريقة لإخبار React بتخطي عملية إعادة الاشتراك إن لم يتغير props.friend.id الذي مررناه إلى ChatAPI.) بشكل مشابه للخطاف useState، تستطيع استعمال أكثر من تأثير في مكون:

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  // ...

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

شرح أوسع: يمكن تعلم المزيد حول الخطاف useEffect في صفحة مخصصة به هي "استعمال خطاف التأثير".

قواعد تخص الخطافات

الخطافات هي دوال في جافاسكربت، ولكنها تفرض تطبيق قاعدتين إضافيتين هما:

  • تستدعى الخطافات فقط في المستوى الأعلى (top level). لا تستدعي الخطافات داخل حلقات تكرارية، أو شروط، أو دوال متشعبة.
  • تستدعى الخطافات فقط من مكونات دالة React (أي React function components). لا تستدعي الخطافات من دوال جافاسكربت العادية. (هنالك مكان آخر صالح يمكن استدعاء الخطافات منه يحدد عند بناء خطافات مخصصة خاصة بك. سنتحدث عن هذا النوع من الخطافات بعد قليل.)

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

شرح أوسع: يمكن قراءة المزيد حول هاتين القاعدتين في صفحة "قواعد استعمال الخطافات".

إنشاء خطافات مخصصة خاصة بك

بعض الأحيان، نحتاج إلى إعادة استعمال بعض المنطق ذي الحالة (stateful logic) بين المكونات. تقليديًّا، يوجد حلان لهذه المشكلة هما: المكونات ذات الترتيب الأعلى، وخاصيات التصيير. تمكنك الخطافات المخصصة من إنجاز هذه المهمة، ولكن دون إضافة المزيد من المكونات إلى شجرتك.

في قسم سابق من هذه الصفحة، عرفنا المكون FriendStatus الذي يستدعي الخطاف useState والخطاف useEffect للاشتراك في حالة اتصال صديق. افترض أننا نريد أيضًا إعادة استعمال منطق هذا الاشتراك في مكون آخر فماذا نفعل؟

أولًا، سنستخرج هذا المنطق إلى خطاف مخصص يدعى useFriendStatus بالشكل التالي:

import React, { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

يأخذ الوسيط friendID، ويعيد حالة الصديق أي إن كان متصلًا أم لا. الآن، يمكننا استعماله من أي مكون نريد:

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

حالة هذه المكونات هي مستقلة كليًّا. الخطافات هي وسيلة لإعادة استعمال المنطق ذي الحالة وليس الحالة نفسها.

انظر أيضًا

 مصادر