useEffect — cleanup funksiya, nima uchun kerak
Muammo: "qoldirilgan" ishlar
useEffect ba'zan uzoq davom etadigan ishlarni boshlaydi — timer, event listener, WebSocket ulanish. Komponent ekrandan ketganda bu ishlar to'xtatilmasa — muammo bo'ladi.
Tasavvur qiling: komponent har soniyada server dan ma'lumot oladi. Foydalanuvchi boshqa sahifaga o'tdi — komponent yo'qoldi. Lekin timer hali ishlayapti, hali server ga so'rov yuborayapti. Komponent yo'q, lekin so'rovlar ketayapti. Bu — memory leak.
Komponent paydo bo'ldi → timer boshlandi (har 1 sek)
Foydalanuvchi ketdi → komponent yo'qoldi
lekin timer hali ishlayapti! ❌
→ xotira sarflanmoqda
→ konsolda xato: "Can't update state on unmounted component"
useEffect ning cleanup funksiyasi aynan shu muammoni hal qiladi.
Cleanup funksiya qanday yoziladi?
useEffect callback funksiya qaytarishi mumkin. Bu qaytarilgan funksiya — cleanup.
useEffect(() => {
// ish boshlash
const timer = setInterval(() => {
console.log('ishlayapti');
}, 1000);
// cleanup — komponent ketganda yoki effect qayta ishlaganda
return () => {
clearInterval(timer); // timerni to'xtatish
};
}, []);
Cleanup qachon chaqiriladi?
┌─────────────────────────────────────────────┐
│ Komponent paydo bo'ldi (mount) │
│ → useEffect ishladi │
│ → timer boshlandi │
└──────────────────┬──────────────────────────┘
│
(foydalanuvchi boshqa sahifaga o'tdi)
│
▼
┌─────────────────────────────────────────────┐
│ Komponent yo'qoldi (unmount) │
│ → cleanup ishladi │
│ → timer to'xtatildi ✅ │
└─────────────────────────────────────────────┘
Dependency bor bo'lsa — cleanup har safar effect qayta ishlaganda ham chaqiriladi:
┌──────────────────────────────────────────────┐
│ userId = 1 → effect ishladi, so'rov boshlandi│
└──────────────────┬───────────────────────────┘
│ userId = 2 ga o'zgardi
▼
┌──────────────────────────────────────────────┐
│ Cleanup: eski so'rov bekor qilindi │
│ Yangi effect: userId=2 uchun so'rov boshlandi│
└──────────────────────────────────────────────┘
Amaliy misollar
Event listener
function OynaTahlili() {
const [kenglik, setKenglik] = useState(window.innerWidth);
useEffect(() => {
function yangilash() {
setKenglik(window.innerWidth);
}
window.addEventListener('resize', yangilash);
return () => {
window.removeEventListener('resize', yangilash); // tozalash
};
}, []);
return <p>Oyna kengligi: {kenglik}px</p>;
}
Cleanup bo'lmasa — komponent yo'qolgandan keyin ham resize event ishlaydi, yo'q komponentning state ini yangilashga urinadi.
AbortController — so'rovni bekor qilish
function UserProfil({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
const controller = new AbortController();
fetch(`/api/users/${userId}`, { signal: controller.signal })
.then(r => r.json())
.then(setUser)
.catch(err => {
if (err.name === 'AbortError') return; // bekor qilindi — normal
console.error(err);
});
return () => {
controller.abort(); // komponent ketsa yoki userId o'zgarse — so'rovni bekor qil
};
}, [userId]);
if (!user) return <p>Yuklanmoqda...</p>;
return <h2>{user.name}</h2>;
}
Nima uchun kerak? userId tez o'zgarsa — masalan, foydalanuvchi tez-tez bosib tursa — bir nechta so'rov ketadi. Qaysi biri oxirgi kelishini bilmaysiz. Eski so'rovlar bekor qilinmasa — eski ma'lumot yangi ma'lumotni ustiga yozishi mumkin.
WebSocket
function ChatXonasi({ xonaId }) {
const [xabarlar, setXabarlar] = useState([]);
useEffect(() => {
const ws = new WebSocket(`wss://chat.example.com/${xonaId}`);
ws.onmessage = (event) => {
setXabarlar(prev => [...prev, JSON.parse(event.data)]);
};
return () => {
ws.close(); // ulanishni yopish
};
}, [xonaId]); // xona o'zgarganda — eski ulanish yopiladi, yangi ochiladi
return (
<ul>
{xabarlar.map((x, i) => <li key={i}>{x.matn}</li>)}
</ul>
);
}
StrictMode va cleanup
Development rejimida React StrictMode bilan ishlayotganda — useEffect ikki marta ishlaydi (mount → cleanup → mount). Bu ataylab qilingan — cleanup to'g'ri yozilganini tekshirish uchun.
StrictMode (development):
Mount → effect ishladi
Cleanup → effect tozalandi
Mount → effect qayta ishladi ← ikkinchi marta
Production:
Mount → effect ishladi ← faqat bir marta
Agar cleanup to'g'ri yozilmagan bo'lsa — bu ikki marta ishlatish muammoni ko'rsatadi. Production da bunday bo'lmaydi, lekin muammo yashirinib qoladi.
Xulosa
useEffect(() => {
// ish boshlash: timer, listener, WebSocket, so'rov
return () => {
// cleanup: to'xtatish, o'chirish, bekor qilish
};
}, []);
Cleanup kerak bo'lgan holatlar:
setInterval/setTimeout→clearInterval/clearTimeoutaddEventListener→removeEventListenerfetch→AbortController.abort()WebSocket→ws.close()
Qoida: useEffect biror narsa boshlaса — cleanup o'sha narsani to'xtatishi kerak.
Keyingi dars
12-dars: useEffect — asinxron funksiya, fetch bilan ishlash