useEffect في React — دليل شامل للتأثيرات الجانبية 2026
دليل عملي مفصل لـ useEffect في React — التأثيرات الجانبية، دورة الحياة، التنظيف، مع تمارين وحلول
في المقال السابق، تعلمت الأحداث والنماذج. الآن سنتعلم useEffect — وهو Hook يسمح بتشغيل تأثيرات جانبية (Side Effects) داخل المكونات.
في هذا الدليل العملي، سنأخذك خطوة بخطوة لفهم useEffect ومتى تستخدمه، مع تمارين وحلول.
ما هو Side Effect؟
Side Effect (تأثير جانبي) هو أي شيء يحدث خارج المكوّنة:
- طلب API (Fetch).
- تعديل DOM مباشرة.
- تعيين مؤقت (setTimeout).
- الاشتراك في event.
- تعديل
document.title.
⚠️ مشكلة: لا يمكن وضع هذه العمليات داخل render (سيسبب مشاكل).
✅ الحل: استخدم useEffect.
لماذا نحتاج useEffect؟
دورة حياة المكوّنة:
1. Mount (التركيب) → useEffect بدون dependencies
2. Update (التحديث) → useEffect مع dependencies
3. Unmount (الإزالة) → useEffect مع cleanup
الصيغة الأساسية
import { useEffect } from "react";
useEffect(() => {
// الكود الذي ينفذ
return () => {
// دالة التنظيف (اختياري)
};
}, [dependencies]);
حالات useEffect
1. بدون dependencies — ينفذ بعد كل render
useEffect(() => {
console.log("ينفذ بعد كل render");
});
⚠️ استخدم بحذر — قد يسبب حلقات لا نهائية.
2. مع مصفوفة فارغة — ينفذ مرة واحدة (Mount)
useEffect(() => {
console.log("ينفذ مرة واحدة فقط");
}, []);
الاستخدام: جلب بيانات أولية، إعداد مكتبة، ...
3. مع dependencies — ينفذ عند تغير القيم
useEffect(() => {
console.log("ينفذ عندما يتغير count");
}, [count]);
الاستخدام: الاستجابة لتغير قيمة معينة.
دالة التنظيف (Cleanup)
useEffect(() => {
const timer = setInterval(() => {
console.log("tick");
}, 1000);
// دالة التنظيف:
return () => {
clearInterval(timer);
};
}, []);
⚠️ مهم: دالة التنظيف:
- تُنفذ عند إزالة المكوّنة.
- أو قبل إعادة تنفيذ useEffect.
أمثلة عملية
1. تعديل عنوان الصفحة
function PageTitle() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `العدد: ${count}`;
}, [count]);
return <button onClick={() => setCount(count + 1)}>+1</button>;
}
كل نقرة تُحدّث عنوان الصفحة!
2. المؤقت (Timer)
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds((s) => s + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <p>مرّ {seconds} ثانية</p>;
}
3. جلب بيانات من API
function Users() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((data) => {
setUsers(data);
setLoading(false);
})
.catch((err) => {
setError(err.message);
setLoading(false);
});
}, []);
if (loading) return <p>جاري التحميل...</p>;
if (error) return <p>خطأ: {error}</p>;
return (
<ul>
{users.map((user: any) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
4. الاعتماد على متغير
function SearchUsers() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
useEffect(() => {
if (query === "") {
setResults([]);
return;
}
const timer = setTimeout(() => {
fetch(`https://api.example.com/search?q=${query}`)
.then((res) => res.json())
.then(setResults);
}, 500);
return () => clearTimeout(timer);
}, [query]);
return (
<div>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<ul>{results.map((r: any) => <li key={r.id}>{r.name}</li>)}</ul>
</div>
);
}
الفكرة: Debouncing — لا يرسل الطلب إلا بعد 500 مللي ثانية من التوقف عن الكتابة.
5. الاشتراك في event
function WindowSize() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return <p>عرض الشاشة: {width}px</p>;
}
useEffect مع TypeScript
1. جلب بيانات
interface User {
id: number;
name: string;
email: string;
}
function Users() {
const [users, setUsers] = useState<User[]>([]);
useEffect(() => {
async function fetchUsers() {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
const data: User[] = await res.json();
setUsers(data);
}
fetchUsers();
}, []);
return <ul>{users.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}
2. دالة التنظيف
useEffect(() => {
const timer: NodeJS.Timeout = setTimeout(() => {
console.log("بعد ثانية");
}, 1000);
return () => clearTimeout(timer);
}, []);
useEffect vs useLayoutEffect
| المعيار | useEffect | useLayoutEffect |
|---|---|---|
| التوقيت | بعد الرسم | قبل الرسم |
| الأداء | ✅ أفضل | ⚠️ يحجب الرسم |
| الاستخدام | معظم الحالات | قياس DOM |
القاعدة: استخدم useEffect دائماً، إلا إذا كنت تقيس DOM.
أخطاء شائعة
🔴 الخطأ 1: حلقة لا نهائية
// ❌ خطأ:
useEffect(() => {
setCount(count + 1); // تحديث state ← إعادة render ← useEffect ← ...
}, [count]);
الحل: لا تحدّث state الذي تعتمد عليه.
🔴 الخطأ 2: نسيان dependencies
// ❌ خطأ: لا يتحدث عند تغير count
useEffect(() => {
document.title = `العدد: ${count}`;
}, []); // ← count مفقود
// ✅ صحيح:
useEffect(() => {
document.title = `العدد: ${count}`;
}, [count]);
🔴 الخطأ 3: نسيان دالة التنظيف
// ❌ خطأ: تسريب ذاكرة
useEffect(() => {
const timer = setInterval(() => {}, 1000);
// لا يوجد cleanup
}, []);
// ✅ صحيح:
useEffect(() => {
const timer = setInterval(() => {}, 1000);
return () => clearInterval(timer);
}, []);
🔴 الخطأ 4: استخدام async مباشرة
// ❌ خطأ:
useEffect(async () => {
const data = await fetch("/api");
}, []);
// ✅ صحيح:
useEffect(() => {
async function fetchData() {
const data = await fetch("/api");
}
fetchData();
}, []);
تمارين عملية
تمرين 1: عنوان ديناميكي
أنشئ مكوّنة تحدّث عنوان الصفحة بعدّاد.
الحل:
function TitleUpdater() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `العدد: ${count}`;
}, [count]);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
تمرين 2: مؤقت تنازلي
أنشئ مؤقتاً يبدأ من 10 وينتهي عند 0.
الحل:
function Countdown() {
const [count, setCount] = useState(10);
useEffect(() => {
if (count === 0) return;
const timer = setTimeout(() => setCount(count - 1), 1000);
return () => clearTimeout(timer);
}, [count]);
return <p>{count === 0 ? "⏰ انتهى!" : count}</p>;
}
تمرين 3: جلب مستخدمين
اجلب قائمة مستخدمين من API واعرضها.
الحل:
function UsersList() {
const [users, setUsers] = useState<any[]>([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then(setUsers);
}, []);
return <ul>{users.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}
تمرين 4: عرض حجم الشاشة
اعرض عرض الشاشة وحدّثه عند تغيير الحجم.
الحل:
function WindowSize() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handler = () => setWidth(window.innerWidth);
window.addEventListener("resize", handler);
return () => window.removeEventListener("resize", handler);
}, []);
return <p>العرض: {width}px</p>;
}
تمرين 5: العدّاد التلقائي
أنشئ عدّاداً يزيد تلقائياً كل ثانية.
الحل:
function AutoCounter() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => setCount((c) => c + 1), 1000);
return () => clearInterval(interval);
}, []);
return <p>{count}</p>;
}
تمرين 6: البحث مع Debounce
أنشئ حقل بحث لا يرسل الطلب إلا بعد 500ms.
الحل:
function DebouncedSearch() {
const [query, setQuery] = useState("");
const [results, setResults] = useState<string[]>([]);
useEffect(() => {
if (!query) {
setResults([]);
return;
}
const timer = setTimeout(() => {
setResults([`نتيجة 1 لـ "${query}"`, `نتيجة 2 لـ "${query}"`]);
}, 500);
return () => clearTimeout(timer);
}, [query]);
return (
<div>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<ul>{results.map((r, i) => <li key={i}>{r}</li>)}</ul>
</div>
);
}
تمرين 7: تسجيل الدخول التلقائي
أنشئ مكوّنة تسجّل رسالة عند mount و unmount.
الحل:
function Logger() {
useEffect(() => {
console.log("🟢 تم التركيب");
return () => {
console.log("🔴 تم الإزالة");
};
}, []);
return <p>افتح Console</p>;
}
تمرين 8: مراقبة الشبكة
أضف مستمعاً لحدث online و offline.
الحل:
function NetworkStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
const online = () => setIsOnline(true);
const offline = () => setIsOnline(false);
window.addEventListener("online", online);
window.addEventListener("offline", offline);
return () => {
window.removeEventListener("online", online);
window.removeEventListener("offline", offline);
};
}, []);
return <p>{isOnline ? "🟢 متصل" : "🔴 غير متصل"}</p>;
}
حل المشاكل الشائعة
🔴 المشكلة 1: حلقة لا نهائية
السبب: تحديث state داخل useEffect مع dependency عليه.
الحل: احذف dependency أو لا تحدّث state.
🔴 المشكلة 2: البيانات لا تُحدّث
السبب: dependency فارغ [].
الحل: أضف المتغيرات التي يعتمد عليها الكود.
🔴 المشكلة 3: تسريب ذاكرة
السبب: عدم تنظيف setInterval/setTimeout.
الحل: استخدم دالة التنظيف.
🔴 المشكلة 4: Warning: Can't perform a React state update on an unmounted component
السبب: تحديث state بعد إزالة المكوّنة.
الحل: استخدم flag:
useEffect(() => {
let isMounted = true;
fetch("/api").then((data) => {
if (isMounted) setData(data);
});
return () => {
isMounted = false;
};
}, []);
جدول useEffect
| الصيغة | متى ينفذ؟ | الاستخدام |
|---|---|---|
useEffect(() => {}) | بعد كل render | ⚠️ نادراً |
useEffect(() => {}, []) | مرة واحدة (Mount) | ✅ جلب بيانات |
useEffect(() => {}, [dep]) | عند تغير dep | ✅ الاستجابة |
return () => {} | عند Unmount | ✅ التنظيف |
قائمة تحقق نهائية
| المهمة | الحالة |
|---|---|
| فهم Side Effects | ⬜ |
| useEffect مع dependency فارغ | ⬜ |
| useEffect مع dependencies | ⬜ |
| دالة التنظيف (Cleanup) | ⬜ |
| جلب بيانات من API | ⬜ |
| Debouncing | ⬜ |
| حل التمارين الثمانية | ⬜ |
ماذا بعد هذا المقال؟
الآن بعد أن أتقنت useEffect، أنت جاهز للمقال التالي:
- القوائم والشروط — العرض الديناميكي.
- React Router — التنقل بين الصفحات.
- Context API — إدارة الحالة العامة.
الخلاصة
في هذا المقال، تعلمت:
- ✅ ما هو Side Effect ولماذا نحتاجه.
- ✅ الصيغة الأساسية لـ useEffect.
- ✅ الحالات الثلاث (Mount, Update, Unmount).
- ✅ دالة التنظيف (Cleanup).
- ✅ جلب بيانات من API.
- ✅ Debouncing.
- ✅ حل 8 تمارين عملية.
تذكر: useEffect قوي لكن لا تبالغ في استخدامه. أحياناً هناك حلول أفضل.
هل أتقنت useEffect؟ شاركنا في التعليقات!
الأحداث والنماذج في React — دليل شامل 2026
القوائم والشروط في React — دليل شامل 2026
📚 مقالات ذات صلة
مشروع React متكامل — تطبيق متجر إلكتروني 2026
مشروع عملي شامل لبناء تطبيق متجر إلكتروني بـ React و TypeScript — مع السلة، البحث، التصفية، والحفظ التلقائي
Context API في React — إدارة الحالة العامة 2026
دليل عملي مفصل لـ Context API في React — إنشاء Context، Provider، useContext، مع تمارين وحلول
React Router — التنقل بين الصفحات في React 2026
دليل عملي مفصل لـ React Router — التنقل، المسارات، الروابط، والمعاملات، مع تمارين وحلول