React Hooks هي وظائف تتيح لك استخدام الحالة والتأثيرات والسياق وميزات React الأخرى في المكونات الوظيفية، دون كتابة فئات. تم تقديمها في React 16.8، وغيرت الخطافات نهج التطوير بشكل جذري، لتحل محل الأنماط المعقدة مثل HOC و render props. وفقًا لـ React، 2024، هناك 15 خطافًا مدمجًا تغطي جميع سيناريوهات إدارة المكونات.
الخلاصة
خطافات React هي مجموعة من الوظائف المدمجة في React التي تتيح لك «التوصيل» بالآليات الداخلية للإطار دون إنشاء فئات. قبل React 16.8، كانت المكونات الوظيفية تستطيع فقط قبول props وإرجاع JSX — أي منطق معقد كان يتطلب مكون فئة.
مشكلة المكونات الفئوية هي تقسيم المنطق المرتبط. الكود المرتبط بكيان واحد (مثل اشتراك WebSocket) كان يُقسَّم قسريًا بين componentDidMount و componentDidUpdate و componentWillUnmount. تحل الخطافات هذا: يتم تجميع كل المنطق في useEffect واحد.
المشكلة الثانية هي إعادة استخدام المنطق. لإعادة استخدام منطق الحالة بين المكونات الفئوية، كان عليك تطبيق HOC (مكونات عالية المستوى) أو render props — كلا النمطين خلقا تداخلًا إضافيًا في شجرة المكونات. تحل الخطافات المخصصة هذه المشكلة دون تداخل.
useState هو الخطاف الأكثر أساسية. يأخذ قيمة أولية ويعيد زوجًا: القيمة الحالية ودالة لتحديثها. على عكس this.setState في الفئات، لا يدمج useState الكائنات — الحالة الجديدة تحل محل الحالة السابقة بالكامل لاستدعاء useState معين.
import React, { useState } from 'react';
function Form() {
const [text, setText] = useState('');
const [submitting, setSubmitting] = useState(false);
async function handleSubmit() {
setSubmitting(true);
await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify({ text })
});
setSubmitting(false);
}
return (
<div>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={handleSubmit}
disabled={submitting}>
{submitting ? 'Saving...' : 'Submit'}
</button>
</div>
);
}
يمكن لمُعيّن useState قبول دالة تستقبل القيمة السابقة وتعيد قيمة جديدة. الصيغة الوظيفية إلزامية عندما تُحسب الحالة الجديدة من الحالة السابقة ومن الممكن استدعاءات setState مجمعة — كل استدعاء سيحصل على القيمة السابقة الحالية.
useEffect هو خطاف لأداء التأثيرات الجانبية في المكونات الوظيفية. يأخذ وسيطين: دالة التأثير ومصفوفة التبعيات. ينفذ React التأثير بعد كل عرض، ولكن عند تحديد التبعيات — فقط عندما تتغير أي منها.
مصفوفة تبعيات فارغة [] تعني أن التأثير يُنفَّذ مرة واحدة بعد العرض الأول (مماثل لـ componentDidMount). إرجاع دالة من التأثير هي عملية تنظيف تُنفَّذ عند إلغاء التثبيت (مماثل لـ componentWillUnmount) وقبل إعادة تنفيذ التأثير.
import React, { useState, useEffect } from 'react';
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const socket = new WebSocket(`wss://chat/${roomId}`);
socket.onmessage = (event) => {
setMessages(prev => [...prev, JSON.parse(event.data)]);
};
// التنظيف - إغلاق المقبس
return () => socket.close();
}, [roomId]);
return <ul>
{messages.map((msg, i) =>
<li key={i}>{msg.text}</li>
)}
</ul>;
}
تبعيات useEffect هي الرابط الوحيد بين التأثير والعالم الخارجي. إذا استخدم التأثير متغيرًا من props أو الحالة، يجب أن يكون في مصفوفة التبعيات. تجاهل هذه القاعدة هو مصدر أخطاء مع closures القديمة.
useContext هو خطاف لقراءة قيم السياق. يأخذ كائن سياق تم إنشاؤه بواسطة React.createContext ويعيد القيمة الحالية. إذا تغير السياق، يُعاد عرض المكون تلقائيًا بالقيمة الجديدة.
قبل useContext، كان الوصول إلى السياق في المكونات الوظيفية يتطلب تغليفًا في Context.Consumer، مما خلق تداخلًا. useContext يلغي Consumer تمامًا — فقط استدعِ الخطاف في أي مكان في المكون الوظيفي.
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('فاتح');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button
className={`btn btn-${theme}`}
>
{theme === 'dark' ? 'moon' : '☀️'}
</button>;
}
useContext ليس بديلاً عن Redux أو الحالة العامة. يوصي React باستخدام السياق للقيم نادرة التغيير — السمة، الإعدادات المحلية، حالة المصادقة. للبيانات التي تُحدَّث بشكل متكرر، كل إعادة عرض ستعيد حساب جميع مستهلكي السياق، مما قد يقلل الأداء.
useReducer هو بديل لـ useState للحالات ذات منطق التحديث المعقد. يأخذ دالة مختزلة وحالة أولية، ويعيد الحالة الحالية ودالة dispatch. تستقبل الدالة المختزلة الحالة الحالية والإجراء، وتعيد حالة جديدة — دالة نقية دون تأثيرات جانبية.
useReducer مفيد بشكل خاص عندما تدير إجراءات متعددة (set, reset, increment, addItem) حالة واحدة. بدلاً من استدعاءات useState متعددة بمُعيِّنات مختلفة — مختزل واحد بأنواع إجراءات واضحة. هذا يجعل المنطق قابلًا للتنبؤ وسهل الاختبار.
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'setStep':
return { ...state, step: action.step };
case 'reset':
return { count: 0, step: 1 };
default:
throw new Error('Unknown action: ' + action.type);
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, {
count: 0,
step: 1
});
return (
<>
<p>Count: {state.count}</p>
<button
onClick={() => dispatch({ type: 'increment' })}>+</button>
<button
onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</>
);
}
useRef ينشئ كائنًا قابلًا للتغيير يستمر بين عمليات إعادة العرض. تغيير current لا يؤدي إلى إعادة عرض. الاستخدامات الرئيسية هي الوصول إلى عناصر DOM وتخزين القيم القابلة للتغيير (المؤقتات، القيم السابقة، نسخ المكتبات).
useMemo يحفظ نتيجة دالة. يعيد حساب القيمة فقط عندما تتغير تبعية ما. استخدمه للعمليات الحسابية المكلفة — تصفية المصفوفات الكبيرة، الفرز، تنسيق البيانات. useCallback هو حالة خاصة من useMemo لحفظ الدوال.
import React, { useRef, useMemo, useCallback } from 'react';
function SearchPage({ items, query, onItemSelect }) {
const inputRef = useRef(null);
// التركيز التلقائي عند التثبيت
useEffect(() => {
inputRef.current?.focus();
}, []);
// حفظ النتيجة المصفاة
const filtered = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase()
.includes(query.toLowerCase())
);
}, [items, query]);
// حفظ رد النداء
const handleSelect = useCallback((id) => {
onItemSelect(id);
}, [onItemSelect]);
return (
<div>
<input ref={inputRef}
value={query}
onChange={e => setQuery(e.target.value)}
/>
<ul>
{filtered.map(item =>
<li key={item.id}>
<button
onClick={() => handleSelect(item.id)}>
{item.name}
</button>
</li>
)}
</ul>
</div>
);
}
قاعدة مهمة: useRef و useMemo و useCallback هي أدوات تحسين، ليست تركيبات إلزامية. لا تغلف كل شيء في useMemo — قس الأداء أولاً. التحسين المبكر يعقّد الكود. يعرض React المكونات بسرعة؛ useMemo مطلوب فقط عندما تكون العمليات الحسابية مكلفة حقًا (O(n²) أو أعلى).
قدم React قاعدتين صارمتين للخطافات. الأولى: استدعِ الخطافات فقط في المستوى الأعلى — ليس داخل الشروط أو الحلقات أو الدوال المتداخلة. الثانية: استدعِ الخطافات فقط من المكونات الوظيفية لـ React أو الخطافات المخصصة. يتحقق إضافة ESLint eslint-plugin-react-hooks تلقائيًا من كلتا القاعدتين.
الخطأ الأكثر شيوعًا هو انتهاك ترتيب استدعاء الخطافات. يعتمد React على ترتيب استدعاء الخطافات بين عمليات إعادة العرض. إذا كان خطاف داخل شرط ولم يتم استدعاؤه في إحدى عمليات إعادة العرض، تنزاح جميع الخطافات اللاحقة، مما يسبب أخطاء.
| الخطأ | ما يحدث | الحل |
|---|---|---|
| خطاف في شرط | ترتيب الخطافات ينكسر | انقل منطق الشرط داخل الخطاف |
| Stale closure | التأثير يستخدم قيمة قديمة | أضف جميع التبعيات إلى المصفوفة |
| حلقة لا نهائية | useEffect بدون تبعيات يغير الحالة | حدد التبعيات أو استخدم useReducer |
| تغيير ref في render | تأثير جانبي في جسم الدالة | انقل التحويرات إلى useEffect |
الخطأ الشائع الثاني هو stale closure في useEffect. إذا استخدم التأثير متغيرًا غير محدد في التبعيات، «يلتقط» التأثير قيمته في وقت إنشاء الإغلاق. الحل هو تضمين جميع المتغيرات المستخدمة دائمًا في مصفوفة التبعيات. إذا كان هناك عدد كبير جدًا من التبعيات، ففكر في تقسيم التأثير إلى عدة أجزاء.
الأسئلة الشائعة
React يعتمد على ترتيب استدعاء الخطافات بين عمليات إعادة العرض. إذا تم استدعاء خطاف داخل شرط ولم يتم تشغيله في إحدى عمليات إعادة العرض، تنزاح جميع الخطافات اللاحقة — يختلط حالتها. تحذر قاعدة ESLint hooks/exhaustive-deps من هذا.
useLayoutEffect متزامن ويُتنفَّذ قبل أن يرسم المتصفح التغييرات. useEffect غير متزامن — بعد الرسم. استخدم useLayoutEffect فقط عندما تحتاج إلى قياس أو تغيير DOM قبل أن يرى المستخدم التغييرات (موضع popover، استعادة التمرير).
استخدم AbortController داخل useEffect. أنشئ controller، مرر signal إلى fetch، واستدعِ controller.abort() في دالة التنظيف. للمتصفحات القديمة، استخدم علامة cancelled: return () => { cancelled = true; } مع فحص قبل setState.
الخطاف المخصص هو دالة JavaScript بالبادئة use تستخدم الخطافات المدمجة. أنشئه عندما يتكرر نفس المنطق مع الخطافات في مكونين أو أكثر. أمثلة: useDebounce, useLocalStorage, useMediaQuery. تحل الخطافات المخصصة محل HOC و render props.
في Strict Mode، يقوم React عمدًا بتثبيت وإلغاء تثبيت المكون مرتين في وضع التطوير للعثور على أخطاء مع التأثيرات غير المُنظَّفة. إذا تمت كتابة التنظيف بشكل صحيح، فإن الاستدعاء المزدوج لا يسبب مشاكل. لا يحدث هذا في وضع الإنتاج.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا