ما هو useEffect؟
خطّاف (Hook) لتنفيذ التأثيرات الجانبية (Side Effects): أشياء خارج عرض الواجهة، مثل جلب بيانات، مؤقّتات، الاشتراك في أحداث، تحديث العنوان.
import { useEffect, useState } from "react";
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `العدد: ${count}`;
});
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
مصفوفة الاعتماديات (Dependency Array) 🔑
تحدّد متى يُعاد تشغيل التأثير:
// 1) بلا مصفوفة → بعد كل عرض (نادرًا ما تريده)
useEffect(() => { ... });
// 2) مصفوفة فارغة → مرة واحدة فقط عند التركيب (mount)
useEffect(() => {
console.log("ظهر المكوّن");
}, []);
// 3) بمتغيّرات → عند تغيّر أيٍّ منها
useEffect(() => {
console.log("تغيّر count");
}, [count]);
🔑 المصفوفة الفارغة
[]= "نفّذ مرة واحدة عند الظهور" — الأشهر لجلب البيانات الأولي.
React يقارن كل اعتمادية بقيمتها السابقة بالمرجع (Object.is). الأرقام والنصوص تُقارن بقيمتها، أمّا كائن أو مصفوفة تُنشأ داخل المكوّن فهي مرجع جديد في كل عرض، فيُعاد تشغيل التأثير كل مرة:
function Results({ query }) {
const options = { query, limit: 10 }; // كائن جديد في كل عرض
useEffect(() => {
fetchResults(options);
}, [options]); // ❌ يتغيّر دائمًا → يعمل بعد كل عرض
useEffect(() => {
fetchResults({ query, limit: 10 });
}, [query]); // ✅ يعتمد على القيمة البسيطة نفسها
}
التنظيف (Cleanup)
أرجِع دالة لتنظيف التأثير عند إزالة المكوّن أو قبل إعادة التشغيل — ضروري للمؤقّتات والاشتراكات:
useEffect(() => {
const id = setInterval(() => {
console.log("تكتك");
}, 1000);
return () => clearInterval(id); // تنظيف!
}, []);
بدون التنظيف، يستمرّ المؤقّت حتى بعد اختفاء المكوّن (تسريب).
متى تُستدعى دالة التنظيف بالضبط؟ مرّتين في حياة التأثير: قبل كل إعادة تشغيل له (لما تتغيّر اعتمادية)، وعند إزالة المكوّن. يعني التأثير القديم يُنظَّف دائمًا قبل أن يبدأ الجديد.
💡 في وضع التطوير مع
<StrictMode>يشغّل React التأثير، ثم ينظّفه، ثم يشغّله مرة ثانية فور التركيب، ليكشف لك أي تأثير نسيت تنظيفه. لو رأيتconsole.logيُطبع مرّتين في التطوير فهذا مقصود، ولا يحدث في الإنتاج.
متى لا تحتاج useEffect؟
useEffect لمزامنة المكوّن مع شيء خارج React (شبكة، مؤقّت، DOM، اشتراك). إذا كانت القيمة تُحسب من props أو state، احسبها مباشرة أثناء العرض:
// ❌ حالة إضافية + تأثير + عرض إضافي
const [fullName, setFullName] = useState("");
useEffect(() => {
setFullName(first + " " + last);
}, [first, last]);
// ✅ تُحسب مباشرة
const fullName = first + " " + last;
مثال: الاستماع لحدث
useEffect(() => {
function handleResize() {
console.log(window.innerWidth);
}
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
الأخطاء الشائعة
- ❌ نسيان مصفوفة الاعتماديات → تشغيل بعد كل عرض (وأحيانًا حلقة لا نهائية).
- ❌ تحديث حالة داخل useEffect بلا اعتماديات صحيحة → حلقة لا نهائية.
- ❌ نسيان التنظيف للمؤقّتات/المستمعين → تسريبات.
- ❌ نسيان متغيّر في المصفوفة → قيم قديمة (stale).
خلاصة
useEffect ينفّذ التأثيرات الجانبية بعد العرض. تحكّم بتوقيته عبر مصفوفة الاعتماديات ([] مرة واحدة، [dep] عند التغيّر)، وأرجِع دالة تنظيف للمؤقّتات والاشتراكات. هو جسرك مع العالم خارج React.