أطلق العنان لقوة المنطق القابل لإعادة الاستخدام في تطبيقات React الخاصة بك باستخدام الخطافات المخصصة. تعلم كيفية إنشائها والاستفادة منها لكتابة كود أنظف وأكثر قابلية للصيانة.
الخطافات المخصصة: أنماط منطقية قابلة لإعادة الاستخدام في React
أحدثت خطافات React ثورة في طريقة كتابتنا لمكونات React من خلال تقديم ميزات الحالة ودورة الحياة للمكونات الوظيفية. من بين الفوائد العديدة التي تقدمها، تبرز الخطافات المخصصة كآلية قوية لاستخلاص وإعادة استخدام المنطق عبر مكونات متعددة. ستتعمق هذه التدوينة في عالم الخطافات المخصصة، مستكشفة فوائدها وإنشائها واستخدامها مع أمثلة عملية.
ما هي الخطافات المخصصة؟
في جوهرها، الخطاف المخصص هو دالة JavaScript تبدأ بكلمة "use" ويمكنها استدعاء خطافات أخرى. تسمح لك باستخلاص منطق المكونات في دوال قابلة لإعادة الاستخدام. هذه طريقة قوية لمشاركة المنطق ذي الحالة، أو الآثار الجانبية، أو السلوكيات المعقدة الأخرى بين المكونات دون اللجوء إلى "render props" أو المكونات عالية الرتبة أو الأنماط المعقدة الأخرى.
الخصائص الرئيسية للخطافات المخصصة:
- قاعدة التسمية: يجب أن تبدأ الخطافات المخصصة بكلمة "use". هذا يشير إلى React بأن الدالة تحتوي على خطافات ويجب أن تتبع قواعد الخطافات.
- قابلية إعادة الاستخدام: الغرض الأساسي هو تغليف المنطق القابل لإعادة الاستخدام، مما يسهل مشاركة الوظائف بين المكونات.
- منطق الحالة: يمكن للخطافات المخصصة إدارة حالتها الخاصة باستخدام خطاف
useState
، مما يسمح لها بتغليف سلوكيات الحالة المعقدة. - الآثار الجانبية: يمكنها أيضًا أداء آثار جانبية باستخدام خطاف
useEffect
، مما يتيح التكامل مع واجهات برمجة التطبيقات الخارجية، وجلب البيانات، والمزيد. - قابلية التركيب: يمكن للخطافات المخصصة استدعاء خطافات أخرى، مما يسمح لك ببناء منطق معقد عن طريق تكوين خطافات أصغر وأكثر تركيزًا.
فوائد استخدام الخطافات المخصصة
تقدم الخطافات المخصصة العديد من المزايا الهامة في تطوير React:
- إعادة استخدام الكود: الفائدة الأكثر وضوحًا هي القدرة على إعادة استخدام المنطق عبر مكونات متعددة. هذا يقلل من تكرار الكود ويعزز قاعدة كود تتبع مبدأ DRY (لا تكرر نفسك).
- تحسين قابلية القراءة: من خلال استخلاص المنطق المعقد في خطافات مخصصة منفصلة، تصبح مكوناتك أنظف وأسهل في الفهم. يظل منطق المكون الأساسي مركزًا على عرض واجهة المستخدم.
- تحسين قابلية الصيانة: عندما يتم تغليف المنطق في خطافات مخصصة، يمكن تطبيق التغييرات وإصلاحات الأخطاء في مكان واحد، مما يقلل من خطر إدخال أخطاء في مكونات متعددة.
- قابلية الاختبار: يمكن اختبار الخطافات المخصصة بسهولة بشكل منفصل، مما يضمن أن المنطق القابل لإعادة الاستخدام يعمل بشكل صحيح بشكل مستقل عن المكونات التي تستخدمه.
- مكونات مبسطة: تساعد الخطافات المخصصة على ترتيب المكونات، مما يجعلها أقل تفصيلاً وأكثر تركيزًا على غرضها الأساسي.
إنشاء أول خطاف مخصص لك
دعنا نوضح إنشاء خطاف مخصص بمثال عملي: خطاف يتتبع حجم النافذة.
مثال: useWindowSize
سيعيد هذا الخطاف العرض والارتفاع الحاليين لنافذة المتصفح. كما سيقوم بتحديث هذه القيم عند تغيير حجم النافذة.
import { useState, useEffect } from 'react';
function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
window.addEventListener('resize', handleResize);
// إزالة مستمع الحدث عند التنظيف
return () => window.removeEventListener('resize', handleResize);
}, []); // المصفوفة الفارغة تضمن تشغيل التأثير عند التحميل فقط
return windowSize;
}
export default useWindowSize;
الشرح:
- استيراد الخطافات اللازمة: نستورد
useState
وuseEffect
من React. - تعريف الخطاف: ننشئ دالة باسم
useWindowSize
، ملتزمين بقاعدة التسمية. - تهيئة الحالة: نستخدم
useState
لتهيئة حالةwindowSize
بالعرض والارتفاع الأوليين للنافذة. - إعداد مستمع الحدث: نستخدم
useEffect
لإضافة مستمع حدث "resize" إلى النافذة. عند تغيير حجم النافذة، تقوم دالةhandleResize
بتحديث حالةwindowSize
. - التنظيف: نعيد دالة تنظيف من
useEffect
لإزالة مستمع الحدث عند إلغاء تحميل المكون. هذا يمنع تسرب الذاكرة. - إرجاع القيم: يعيد الخطاف كائن
windowSize
، الذي يحتوي على العرض والارتفاع الحاليين للنافذة.
استخدام الخطاف المخصص في مكون
الآن بعد أن أنشأنا خطافنا المخصص، دعنا نرى كيفية استخدامه في مكون React.
import React from 'react';
import useWindowSize from './useWindowSize';
function MyComponent() {
const { width, height } = useWindowSize();
return (
عرض النافذة: {width}px
ارتفاع النافذة: {height}px
);
}
export default MyComponent;
الشرح:
- استيراد الخطاف: نستورد الخطاف المخصص
useWindowSize
. - استدعاء الخطاف: نستدعي خطاف
useWindowSize
داخل المكون. - الوصول إلى القيم: نقوم بتفكيك الكائن المعاد للحصول على قيم
width
وheight
. - عرض القيم: نعرض قيم العرض والارتفاع في واجهة المستخدم للمكون.
أي مكون يستخدم useWindowSize
سيتم تحديثه تلقائيًا عند تغيير حجم النافذة.
أمثلة أكثر تعقيدًا
دعنا نستكشف بعض حالات الاستخدام الأكثر تقدمًا للخطافات المخصصة.
مثال: useLocalStorage
يسمح لك هذا الخطاف بتخزين واسترداد البيانات بسهولة من التخزين المحلي (local storage).
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// حالة لتخزين قيمتنا
// تمرير القيمة الأولية إلى useState بحيث يتم تنفيذ المنطق مرة واحدة فقط
const [storedValue, setStoredValue] = useState(() => {
try {
// الحصول على القيمة من التخزين المحلي بواسطة المفتاح
const item = window.localStorage.getItem(key);
// تحليل JSON المخزن أو إرجاع القيمة الأولية في حالة عدم وجوده
return item ? JSON.parse(item) : initialValue;
} catch (error) {
// في حالة حدوث خطأ، قم أيضًا بإرجاع القيمة الأولية
console.log(error);
return initialValue;
}
});
// إرجاع نسخة مغلفة من دالة setter الخاصة بـ useState والتي ...
// ... تحتفظ بالقيمة الجديدة في localStorage.
const setValue = (value) => {
try {
// السماح بأن تكون القيمة دالة حتى يكون لدينا نفس واجهة برمجة التطبيقات مثل useState
const valueToStore = value instanceof Function ? value(storedValue) : value;
// الحفظ في التخزين المحلي
window.localStorage.setItem(key, JSON.stringify(valueToStore));
// حفظ الحالة
setStoredValue(valueToStore);
} catch (error) {
// التنفيذ الأكثر تقدمًا سيتعامل مع حالة الخطأ
console.log(error);
}
};
useEffect(() => {
try {
const item = window.localStorage.getItem(key);
setStoredValue(item ? JSON.parse(item) : initialValue);
} catch (error) {
console.log(error);
}
}, [key, initialValue]);
return [storedValue, setValue];
}
export default useLocalStorage;
الاستخدام:
import React from 'react';
import useLocalStorage from './useLocalStorage';
function MyComponent() {
const [name, setName] = useLocalStorage('name', 'Guest');
return (
مرحبًا, {name}!
setName(e.target.value)}
/>
);
}
export default MyComponent;
مثال: useFetch
يغلف هذا الخطاف منطق جلب البيانات من واجهة برمجة التطبيقات (API).
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const json = await response.json();
setData(json);
setLoading(false);
} catch (error) {
setError(error);
setLoading(false);
}
}
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
الاستخدام:
import React from 'react';
import useFetch from './useFetch';
function MyComponent() {
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/todos/1');
if (loading) return جار التحميل...
;
if (error) return خطأ: {error.message}
;
return (
العنوان: {data.title}
مكتمل: {data.completed ? 'نعم' : 'لا'}
);
}
export default MyComponent;
أفضل الممارسات للخطافات المخصصة
لضمان فعالية خطافاتك المخصصة وقابليتها للصيانة، اتبع أفضل الممارسات التالية:
- اجعلها مركزة: يجب أن يكون لكل خطاف مخصص غرض واحد محدد جيدًا. تجنب إنشاء خطافات معقدة للغاية تحاول فعل الكثير.
- وثق خطافاتك: قدم توثيقًا واضحًا وموجزًا لكل خطاف مخصص، يشرح غرضه ومدخلاته ومخرجاته.
- اختبر خطافاتك: اكتب اختبارات وحدة لخطافاتك المخصصة للتأكد من أنها تعمل بشكل صحيح وموثوق.
- استخدم أسماء وصفية: اختر أسماء وصفية لخطافاتك المخصصة تشير بوضوح إلى غرضها.
- تعامل مع الأخطاء بأناقة: قم بتنفيذ معالجة الأخطاء داخل خطافاتك المخصصة لمنع السلوك غير المتوقع وتقديم رسائل خطأ مفيدة.
- ضع في اعتبارك قابلية إعادة الاستخدام: صمم خطافاتك المخصصة مع مراعاة قابلية إعادة الاستخدام. اجعلها عامة بما يكفي لاستخدامها في مكونات متعددة.
- تجنب التجريد المفرط: لا تنشئ خطافات مخصصة لمنطق بسيط يمكن التعامل معه بسهولة داخل المكون. استخلص فقط المنطق القابل لإعادة الاستخدام والمعقد حقًا.
الأخطاء الشائعة التي يجب تجنبها
- كسر قواعد الخطافات: استدعِ الخطافات دائمًا على المستوى الأعلى لدالة الخطاف المخصصة الخاصة بك واستدعها فقط من مكونات React الوظيفية أو الخطافات المخصصة الأخرى.
- تجاهل التبعيات في useEffect: تأكد من تضمين جميع التبعيات اللازمة في مصفوفة التبعية لخطاف
useEffect
لمنع الإغلاقات القديمة (stale closures) والسلوك غير المتوقع. - إنشاء حلقات لا نهائية: كن حذرًا عند تحديث الحالة داخل خطاف
useEffect
، حيث يمكن أن يؤدي ذلك بسهولة إلى حلقات لا نهائية. تأكد من أن التحديث شرطي ويعتمد على التغييرات في التبعيات. - نسيان التنظيف: قم دائمًا بتضمين دالة تنظيف في
useEffect
لإزالة مستمعي الأحداث وإلغاء الاشتراكات وأداء مهام التنظيف الأخرى لمنع تسرب الذاكرة.
الأنماط المتقدمة
تركيب الخطافات المخصصة
يمكن تركيب الخطافات المخصصة معًا لإنشاء منطق أكثر تعقيدًا. على سبيل المثال، يمكنك دمج خطاف useLocalStorage
مع خطاف useFetch
للحفاظ على البيانات التي تم جلبها تلقائيًا في التخزين المحلي.
مشاركة المنطق بين الخطافات
إذا كانت هناك خطافات مخصصة متعددة تشترك في منطق مشترك، يمكنك استخلاص هذا المنطق في دالة مساعدة منفصلة وإعادة استخدامه في كلا الخطافين.
استخدام السياق مع الخطافات المخصصة
يمكن استخدام الخطافات المخصصة بالاقتران مع React Context للوصول إلى الحالة العامة وتحديثها. يتيح لك ذلك إنشاء مكونات قابلة لإعادة الاستخدام تكون على دراية بالحالة العامة للتطبيق ويمكنها التفاعل معها.
أمثلة من العالم الحقيقي
فيما يلي بعض الأمثلة على كيفية استخدام الخطافات المخصصة في تطبيقات العالم الحقيقي:
- التحقق من صحة النماذج: إنشاء خطاف
useForm
للتعامل مع حالة النموذج والتحقق من صحته وتقديمه. - المصادقة: تنفيذ خطاف
useAuth
لإدارة مصادقة المستخدم وتفويضه. - إدارة السمات: تطوير خطاف
useTheme
للتبديل بين السمات المختلفة (فاتح، داكن، إلخ). - تحديد الموقع الجغرافي: بناء خطاف
useGeolocation
لتتبع الموقع الحالي للمستخدم. - اكتشاف التمرير: إنشاء خطاف
useScroll
لاكتشاف متى قام المستخدم بالتمرير إلى نقطة معينة في الصفحة.
مثال: خطاف useGeolocation للتطبيقات متعددة الثقافات مثل الخرائط أو خدمات التوصيل
import { useState, useEffect } from 'react';
function useGeolocation() {
const [location, setLocation] = useState({
latitude: null,
longitude: null,
error: null,
});
useEffect(() => {
if (!navigator.geolocation) {
setLocation({
latitude: null,
longitude: null,
error: 'الموقع الجغرافي غير مدعوم من قبل هذا المتصفح.',
});
return;
}
const watchId = navigator.geolocation.watchPosition(
(position) => {
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null,
});
},
(error) => {
setLocation({
latitude: null,
longitude: null,
error: error.message,
});
}
);
return () => navigator.geolocation.clearWatch(watchId);
}, []);
return location;
}
export default useGeolocation;
الخاتمة
الخطافات المخصصة هي أداة قوية لكتابة كود React أنظف وأكثر قابلية لإعادة الاستخدام والصيانة. من خلال تغليف المنطق المعقد في خطافات مخصصة، يمكنك تبسيط مكوناتك وتقليل تكرار الكود وتحسين الهيكل العام لتطبيقاتك. احتضن الخطافات المخصصة وأطلق العنان لإمكاناتها لبناء تطبيقات React أكثر قوة وقابلية للتوسع.
ابدأ بتحديد المناطق في قاعدة الكود الحالية الخاصة بك حيث يتم تكرار المنطق عبر مكونات متعددة. بعد ذلك، قم بإعادة هيكلة هذا المنطق في خطافات مخصصة. بمرور الوقت، ستبني مكتبة من الخطافات القابلة لإعادة الاستخدام التي ستسرع عملية التطوير وتحسن جودة الكود الخاص بك.
تذكر أن تتبع أفضل الممارسات، وتتجنب الأخطاء الشائعة، وتستكشف الأنماط المتقدمة لتحقيق أقصى استفادة من الخطافات المخصصة. مع الممارسة والخبرة، ستصبح محترفًا في استخدام الخطافات المخصصة ومطور React أكثر فعالية.