useEffect — nima, qachon ishlaydi, dependency array

~20 daq · React · Hooks — Asosiy

Muammo: render vaqtida qilish mumkin bo'lmagan ishlar

Ba'zi ishlar komponent chizilayotganda emas, chizilgandan keyin bajarilishi kerak:

  • Server dan ma'lumot olish
  • document.title ni o'zgartirish
  • Timer o'rnatish
  • Event listener qo'shish

Nima uchun render vaqtida emas? Render — bu JSX qaytarish. Bu vaqtda DOM hali tayyor emas. Server so'rovi render ni to'xtatib qo'yadi. Bunday "yon ta'sirlar" (side effects) render dan tashqarida bo'lishi kerak.

useEffect — aynan shu "render dan keyin ishlaydigan" joy.


useEffect qanday ishlatiladi?

import { useState, useEffect } from 'react';

function App() {
  const [son, setSon] = useState(0);

  useEffect(() => {
    document.title = `Bosimlar: ${son}`;
  });

  return (
    <div>
      <p>{son}</p>
      <button onClick={() => setSon(son + 1)}>+1</button>
    </div>
  );
}
Render bo'ldi → useEffect ishladi → document.title yangilandi

Lekin bu holda useEffect har bir render da ishlaydi. Ko'pincha bu kerak emas — faqat ma'lum narsa o'zgarganda ishlashi kerak.


Dependency array — qachon ishlashini boshqarish

useEffect ning ikkinchi argumenti — dependency array (bog'liqliklar massivi).

useEffect(() => {
  // ish
}, [bog'liqliklar]);

Uch holat bor:

useEffect(() => { ... })
  → har bir render da ishlaydi

useEffect(() => { ... }, [])
  → faqat birinchi render da ishlaydi (mount)

useEffect(() => { ... }, [qiymat])
  → birinchi render da + qiymat o'zgarganda ishlaydi

Vizual tushuntirish

[] — bo'sh massiv:
  Mount → ✅ ishlaydi
  Re-render → ❌ ishlamaydi
  ─────────────────────────────
  Foydalanish: server so'rovi, event listener

[qiymat] — qiymat bilan:
  Mount → ✅ ishlaydi
  qiymat o'zgardi → ✅ ishlaydi
  boshqa state o'zgardi → ❌ ishlamaydi
  ─────────────────────────────
  Foydalanish: qiymatga bog'liq so'rov, hisoblash

(yo'q) — massiv yo'q:
  Har bir render → ✅ ishlaydi
  ─────────────────────────────
  Kamdan-kam kerak bo'ladi

Amaliy misollar

Server dan ma'lumot olish

function UserRoyxati() {
  const [users, setUsers] = useState([]);
  const [yuklanmoqda, setYuklanmoqda] = useState(true);

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/users')
      .then(r => r.json())
      .then(data => {
        setUsers(data);
        setYuklanmoqda(false);
      });
  }, []); // [] — faqat bir marta, komponent paydo bo'lganda

  if (yuklanmoqda) return <p>Yuklanmoqda...</p>;

  return (
    <ul>
      {users.map(u => <li key={u.id}>{u.name}</li>)}
    </ul>
  );
}

Qiymatga bog'liq so'rov

function UserProfil({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(r => r.json())
      .then(setUser);
  }, [userId]); // userId o'zgarganda qayta so'rov yuboradi

  if (!user) return <p>Yuklanmoqda...</p>;
  return <h2>{user.name}</h2>;
}

userId props o'zgarsa — masalan, boshqa foydalanuvchi profiliga o'tilsa — useEffect qayta ishlaydi va yangi ma'lumot oladi.

document.title

function Sahifa({ sarlavha }) {
  useEffect(() => {
    document.title = sarlavha;
  }, [sarlavha]); // sarlavha o'zgarganda yangilanadi

  return <h1>{sarlavha}</h1>;
}

Tez-tez uchraydigan xatolar

Dependency array ni unutish:

// ❌ Har render da ishlaydi — cheksiz loop xavfi
useEffect(() => {
  fetch('/api/users').then(...);
});

// ✅ Faqat bir marta
useEffect(() => {
  fetch('/api/users').then(...);
}, []);

Dependency array ni to'liq to'ldirmaslik:

function Komponent({ id }) {
  useEffect(() => {
    fetch(`/api/${id}`).then(...); // id ishlatilmoqda
  }, []); // ❌ id dependency da yo'q!
  // id o'zgarganda useEffect qayta ishlamaydi
}

// ✅ To'g'ri
useEffect(() => {
  fetch(`/api/${id}`).then(...);
}, [id]);

💡 Qoida oddiy: useEffect ichida ishlatilgan har bir o'zgaruvchi dependency array da bo'lishi kerak. ESLint react-hooks/exhaustive-deps qoidasi buni avtomatik tekshiradi.

useEffect ichida async funksiya:

// ❌ Xato — useEffect callback async bo'lmasligi kerak
useEffect(async () => {
  const data = await fetch('/api/users');
});

// ✅ To'g'ri — ichida alohida async funksiya
useEffect(() => {
  async function olish() {
    const res = await fetch('/api/users');
    const data = await res.json();
    setUsers(data);
  }
  olish();
}, []);

Nima uchun? useEffect callback Promise qaytarmasligi kerak — u cleanup funksiya qaytarishi mumkin (keyingi darsda). async funksiya har doim Promise qaytaradi.


Xulosa

useEffect(() => {
  // render dan keyin ishlaydigan kod
}, [bog'liqliklar]);

[]        → faqat mount da (bir marta)
[a, b]    → mount da + a yoki b o'zgarganda
(yo'q)    → har render da
  • Server so'rovlari, DOM o'zgartirishlar, timer — useEffect ichida
  • Dependency array ni to'g'ri to'ldiring — ishlatilgan har bir o'zgaruvchi
  • async/await uchun ichida alohida funksiya yozing

Keyingi dars

11-dars: useEffect — cleanup funksiya, nima uchun kerak