إنشاء الهوكس المخصصة (Building Custom Hooks)
| العنصر الهندسي | الهوكس المخصصة Custom Hooks | الدوال العادية Utility Functions |
|---|---|---|
| إمكانية استدعاء هوكس أخرى | نعم (يمكنها استخدام useState و useEffect و useRef وغيرها) | لا (لا يمكنها استدعاء هوكس ريأكت نهائياً) |
| الهدف الأساسي | مشاركة المنطق التفاعلي وحالة المكونات (Stateful Logic) | مشاركة الحسابات والمنطق البرمجي المجرد (Stateless Logic) |
| قواعد التسمية الصارمة | تبدأ حتماً بكلمة use الصغيرة (مثل useFetch و useLocalStorage) | تسمية عادية بحسب وظيفة الدالة (مثل formatDate) |
| تأثيرها على الرندرة | تغير الحالة داخل الهوك يسبب إعادة رندرة للمكون المستدعي له | لا تؤثر على رندرة المكونات وتعمل كمعالجة بيانات فقط |
كلما كبر حجم التطبيق، ستلاحظ تكرار منطق تفاعلي معين داخل أكثر من مكون. على سبيل المثال: جلب البيانات من الـ API، أو القراءة والتخزين في الـ localStorage، أو تتبع حالة اتصال الإنترنت.
تتيح لك الهوكس المخصصة (Custom Hooks) استخلاص هذا المنطق المتكرر ونقله إلى دالة مستقلة وقابلة لإعادة الاستخدام في أي مكان، مع الاحتفاظ بالقدرة الكاملة على استخدام هوكس ريأكت الأساسية مثل useState و useEffect داخلها.
بدلاً من كتابة useEffect وحالات التحميل والخطأ في كل صفحة نجلب منها بيانات، ننشئ هوك مخصص موحد:
useFetch.js
| 1 | import { useState, useEffect } from 'react'; |
| 2 | |
| 3 | function useFetch(url) { |
| 4 | const [data, setData] = useState(null); |
| 5 | const [isLoading, setIsLoading] = useState(true); |
| 6 | const [error, setError] = useState(null); |
| 7 | |
| 8 | useEffect(() => { |
| 9 | setIsLoading(true); |
| 10 | fetch(url) |
| 11 | .then((res) => { |
| 12 | if (!res.ok) throw new Error('فشل جلب البيانات'); |
| 13 | return res.json(); |
| 14 | }) |
| 15 | .then((data) => setData(data)) |
| 16 | .catch((err) => setError(err.message)) |
| 17 | .finally(() => setIsLoading(false)); |
| 18 | }, [url]); |
| 19 | |
| 20 | // إرجاع الأغراض ككائن أو مصفوفة |
| 21 | return { data, isLoading, error }; |
| 22 | } |
| 23 | |
| 24 | export default useFetch; |
| 1 | import useFetch from './useFetch'; |
| 2 | |
| 3 | function CourseList() { |
| 4 | // اختصار عشرات الأسطر في سطر واحد نظيف! |
| 5 | const { data: courses, isLoading, error } = useFetch('https://api.nouvil.net/courses'); |
| 6 | |
| 7 | if (isLoading) return <p>جاري التحميل...</p>; |
| 8 | if (error) return <p>خطأ: {error}</p>; |
| 9 | |
| 10 | return ( |
| 11 | <ul> |
| 12 | {courses?.map((c) => ( |
| 13 | <li key={c.id}>{c.title}</li> |
| 14 | ))} |
| 15 | </ul> |
| 16 | ); |
| 17 | } |
| 1 | import { useState, useEffect } from 'react'; |
| 2 | |
| 3 | function useLocalStorage(key, initialValue) { |
| 4 | const [value, setValue] = useState(() => { |
| 5 | const saved = localStorage.getItem(key); |
| 6 | return saved ? JSON.parse(saved) : initialValue; |
| 7 | }); |
| 8 | |
| 9 | useEffect(() => { |
| 10 | localStorage.setItem(key, JSON.stringify(value)); |
| 11 | }, [key, value]); |
| 12 | |
| 13 | return [value, setValue]; |
| 14 | } |