تعلم كيفية ترويض استخدام رد الفعل Callback Hook
نشرت: 2022-04-27ليس سراً أن React.js قد أصبحت شائعة على نطاق واسع في السنوات الأخيرة. إنها الآن مكتبة JavaScript المفضلة للعديد من أبرز لاعبي الإنترنت ، بما في ذلك Facebook و WhatsApp.
كان أحد الأسباب الرئيسية لارتفاعه هو إدخال الخطافات في الإصدار 16.8. تتيح لك React hooks الاستفادة من وظيفة React دون كتابة مكونات الصنف. أصبحت المكونات الوظيفية ذات الخطافات الآن هي البنية الأساسية للمطورين للعمل مع React.
في منشور المدونة هذا ، سنبحث بشكل أعمق في خطاف واحد محدد - useCallback
- لأنه يمس جزءًا أساسيًا من البرمجة الوظيفية المعروفة باسم memoization. ستعرف بالضبط كيف ومتى تستخدم خطاف useCallback
والاستفادة القصوى من قدرات تحسين الأداء.
مستعد؟ دعنا نتعمق!
ما هو Memoization؟
Memoization هو عندما تقوم دالة معقدة بتخزين مخرجاتها ، لذلك في المرة التالية يتم استدعاؤها بنفس المدخلات. إنه مشابه للتخزين المؤقت ، ولكن على المستوى المحلي. يمكنه تخطي أي حسابات معقدة وإرجاع الإخراج بشكل أسرع كما تم حسابه بالفعل.
يمكن أن يكون لهذا تأثير كبير على تخصيص الذاكرة وأدائها ، وهذا الضغط هو ما يهدف الخطاف useCallback
للتخفيف منه.
استخدام React's useCallback مقابل useMemo
في هذه المرحلة ، من الجدير بالذكر أن أزواج useCallback
بشكل جيد مع خطاف آخر يسمى useMemo
. سنناقشهما معًا ، لكن في هذه المقالة ، سنركز على useCallback
باعتباره الموضوع الرئيسي.
يتمثل الاختلاف الرئيسي في أن useMemo
تُرجع قيمة محفوظة ، بينما تُرجع useCallback
دالة تم حفظها. هذا يعني أن useMemo
تُستخدم لتخزين قيمة محسوبة ، بينما تُرجع useCallback
وظيفة يمكنك استدعاءها لاحقًا.
ستعيد لك هذه الخطافات نسخة مخبأة ما لم تتغير إحدى تبعياتها (مثل الحالة أو الدعائم).
دعنا نلقي نظرة على الوظيفتين في العمل:
import { useMemo, useCallback } from 'react' const values = [3, 9, 6, 4, 2, 1] // This will always return the same value, a sorted array. Once the values array changes then this will recompute. const memoizedValue = useMemo(() => values.sort(), [values]) // This will give me back a function that can be called later on. It will always return the same result unless the values array is modified. const memoizedFunction = useCallback(() => values.sort(), [values])
مقتطف الشفرة أعلاه هو مثال مفتعل ولكنه يوضح الفرق بين استرجاعي النداء:
-
memoizedValue
المصفوفة[1, 2, 3, 4, 6, 9]
. طالما بقي متغير القيم ، فسيتم حفظ القيمة ، ولن يتم إعادةmemoizedValue
أبدًا. - ستكون دالة
memoizedFunction
دالة تقوم بإرجاع المصفوفة[1, 2, 3, 4, 6, 9]
.
ما هو رائع في هاتين الاسترجاعتين هو أنهما يتم تخزينهما مؤقتًا ويتوقفان حتى يتغير مصفوفة التبعية. هذا يعني أنه في العرض ، لن يتم جمع القمامة.
للتغريدالتقديم والرد
لماذا يعد التذكر مهمًا عندما يتعلق الأمر بـ React؟
يتعلق الأمر بكيفية عرض React لمكوناتك. يستخدم React DOM الظاهري المخزن في الذاكرة لمقارنة البيانات وتحديد ما سيتم تحديثه.
يساعد DOM الافتراضي على التفاعل مع الأداء ويحافظ على سرعة تطبيقك. بشكل افتراضي ، إذا تغيرت أي قيمة في المكون الخاص بك ، فسيتم إعادة تصيير المكون بأكمله. هذا يجعل React "تفاعليًا" لإدخال المستخدم ويسمح للشاشة بالتحديث دون إعادة تحميل الصفحة.
لا تريد عرض المكون الخاص بك لأن التغييرات لن تؤثر على هذا المكون. هذا هو المكان الذي يكون فيه التحفيظ من خلال useCallback
و useMemo
مفيدًا.
عندما يعيد React تصيير المكون الخاص بك ، فإنه يعيد أيضًا إنشاء الوظائف التي أعلنتها داخل المكون الخاص بك.
لاحظ أنه عند مقارنة تكافؤ دالة بوظيفة أخرى ، ستكون دائمًا خاطئة. لأن الوظيفة هي أيضًا كائن ، فإنها ستساوي نفسها فقط:
// these variables contain the exact same function but they are not equal const hello = () => console.log('Hello Matt') const hello2 = () => console.log('Hello Matt') hello === hello2 // false hello === hello // true
بعبارة أخرى ، عندما تعيد React تصيير المكوِّن الخاص بك ، سترى أي وظائف معلنة في المكون الخاص بك على أنها وظائف جديدة.
هذا جيد في معظم الأوقات ، والوظائف البسيطة سهلة الحساب ولن تؤثر على الأداء. ولكن في الأوقات الأخرى التي لا تريد فيها اعتبار الوظيفة كوظيفة جديدة ، يمكنك الاعتماد على useCallback
لمساعدتك.
قد تفكر ، "متى لا أريد أن يُنظر إلى وظيفة على أنها وظيفة جديدة؟" حسنًا ، هناك حالات معينة يكون فيها useCallback
أكثر منطقية:
- أنت تقوم بتمرير الوظيفة إلى مكون آخر يتم حفظه أيضًا (
useMemo
) - وظيفتك لها حالة داخلية يجب أن تتذكرها
- وظيفتك هي تبعية لخطاف آخر ، مثل
useEffect
على سبيل المثال
فوائد الأداء لاستخدام React
عند استخدام useCallback
بشكل مناسب ، يمكن أن يساعد في تسريع تطبيقك ومنع المكونات من إعادة العرض إذا لم تكن بحاجة إلى ذلك.
لنفترض ، على سبيل المثال ، أن لديك مكونًا يجلب كمية كبيرة من البيانات ويكون مسؤولاً عن عرض تلك البيانات في شكل مخطط أو رسم بياني ، مثل هذا:
افترض أن المكون الرئيسي لمكون تصور البيانات يعيد عرضه ، لكن الخصائص أو الحالة التي تم تغييرها لا تؤثر على هذا المكون. في هذه الحالة ، ربما لا تريد أو تحتاج إلى إعادة عرضه وإعادة جلب جميع البيانات. يمكن أن يؤدي تجنب إعادة التقديم وإعادة الجلب إلى توفير النطاق الترددي للمستخدم وتوفير تجربة مستخدم أكثر سلاسة.
عيوب استخدام React
على الرغم من أن هذا الخطاف يمكن أن يساعدك على تحسين الأداء ، إلا أنه يأتي أيضًا مع عيوبه. بعض الأشياء التي يجب مراعاتها قبل استخدام useCallback
(و useMemo
) هي:
- جمع البيانات المهملة: سيتم التخلص من الوظائف الأخرى التي لم يتم حفظها في الذاكرة بالفعل بواسطة React لتحرير الذاكرة.
- تخصيص الذاكرة: على غرار جمع البيانات المهملة ، كلما زادت وظائف الذاكرة لديك ، زادت الذاكرة المطلوبة. بالإضافة إلى ذلك ، في كل مرة تستخدم فيها عمليات الاسترجاعات هذه ، هناك مجموعة من التعليمات البرمجية داخل React تحتاج إلى استخدام المزيد من الذاكرة لتزويدك بالإخراج المخزن مؤقتًا.
- تعقيد التعليمات البرمجية: عندما تبدأ وظائف الالتفاف في هذه الخطافات ، فإنك تزيد فورًا من تعقيد التعليمات البرمجية الخاصة بك. يتطلب الأمر الآن مزيدًا من الفهم لسبب استخدام هذه الخطافات والتأكيد على استخدامها بشكل صحيح.
إن إدراكك للمخاطر المذكورة أعلاه يمكن أن يوفر عليك صداع التعثر بها بنفسك. عند التفكير في استخدام useCallback
، تأكد من أن مزايا الأداء ستفوق العيوب.
رد الفعل useCallback مثال
يوجد أدناه إعداد بسيط مع مكون زر ومكون عداد. يحتوي العداد على جزأين من الحالة ويعرض مكونين للأزرار ، كل منهما سيقوم بتحديث جزء منفصل من حالة مكونات العداد.
يتضمّن مكوِّن الزر خاصيتين: handleClick
و name. في كل مرة يتم فيها تقديم الزر ، سيتم تسجيل الدخول إلى وحدة التحكم.
import { useCallback, useState } from 'react' const Button = ({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> } const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) return ( <> {countOne} {countTwo} <Button handleClick={() => setCountOne(countOne + 1)} name="button1" /> <Button handleClick={() => setCountTwo(countTwo + 1)} name="button1" /> </> ) }
في هذا المثال ، كلما نقرت على أي من الزرين ، سترى هذا في وحدة التحكم:
// counter rendered // button1 rendered // button2 rendered
الآن ، إذا طبقنا useCallback
على وظائف handleClick
بلف الزر في React.memo
، يمكننا أن نرى ما الذي يوفره لنا useCallback
. React.memo
مشابه لـ useMemo
ويسمح لنا بتذكر أحد المكونات.
import { useCallback, useState } from 'react' const Button = React.memo(({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> }) const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) const memoizedSetCountOne = useCallback(() => setCountOne(countOne + 1), [countOne) const memoizedSetCountTwo = useCallback(() => setCountTwo(countTwo + 1), [countTwo]) return ( <> {countOne} {countTwo} <Button handleClick={memoizedSetCountOne} name="button1" /> <Button handleClick={memoizedSetCountTwo} name="button1" /> </> ) }
الآن عندما نضغط على أي من الأزرار ، سنرى فقط الزر الذي نقرنا عليه لتسجيل الدخول إلى وحدة التحكم:
// counter rendered // button1 rendered // counter rendered // button2 rendered
لقد طبقنا الذاكرة على مكون الزر الخاص بنا ، وتعتبر قيم الخاصية التي تم تمريرها إليه متساوية. يتم تخزين handleClick
مؤقتًا وسيُنظر إليهما على أنهما نفس الوظيفة بواسطة React حتى تتغير قيمة عنصر في مصفوفة التبعية (مثل countOne
و countTwo
).
ملخص
بقدر ما هو رائع مثل useCallback
و useMemo
، تذكر أن لديهم حالات استخدام محددة - يجب ألا تغلف كل وظيفة بهذه الخطافات. إذا كانت الوظيفة معقدة حسابيًا ، فإن تبعية خطاف آخر أو خاصية تم تمريرها إلى مكون محفوظ هي مؤشرات جيدة قد ترغب في الوصول إليها من أجل useCallback
.
نأمل أن تساعدك هذه المقالة في فهم وظيفة React المتقدمة هذه وساعدتك على اكتساب المزيد من الثقة مع البرمجة الوظيفية على طول الطريق!