IlmHamroh
JavaScript Full-stack/17-qism. React asoslari32/45-dars17 daqiqa
Mundarija (23)

Effektda ma'lumot olish: fetch + useEffect to'liq shakli va uning cheklovlari

Qisqacha: useEffect ichida fetch qilish ishlaydi, lekin to'g'ri yozish uchun ko'p narsa kerak: yuklanish, xato va ma'lumot holati, res.ok tekshiruvi, AbortController bilan poygadan himoya. Shunda ham uchta muammo qoladi. Kesh yo'q — har qaytishda qayta yuklanadi. Sharshara (waterfall) — ota yuklab bo'lgach, bola so'rov boshlaydi: o'lchovda 0,56 s o'rniga 1,09 s. Server'da ishlamaydi — effekt faqat brauzerda bajariladi. Shuning uchun real ilovalarda ma'lumot olishni kutubxona (TanStack Query, SWR) yoki framework (Next.js, React Router) zimmasiga beradi. Effektdagi fetch — o'rganish, kichik ilova va sodda holatlar uchun.

Bu darsda

  • fetch + useEffect ning to'liq, to'g'ri shaklini yoza olasiz: uch holat, xato, bekor qilish.
  • Kesh yo'qligini so'rovlar hisobida ko'rasiz.
  • Sharshara (waterfall) nima ekanini haqiqiy o'lchov bilan tushuntirasiz va Promise.all bilan tezlashtirasiz.
  • Effektda ma'lumot olishning qolgan cheklovlarini (StrictMode, server, takror so'rov) sanab bera olasiz.
  • Keyingi qismlarda ma'lumot olishni qaysi vositalar o'z zimmasiga olishini bilasiz.

Oldin bilishingiz kerak: useEffect III: cleanup va AbortController, Sizga effekt kerak emas, fetch asoslari, Promise kombinatorlari.

1. Nega bu kerak?

useEffect III darsida menyuni serverdan oldik va poygani AbortController bilan tuzatdik. Jasur aka xursand: "Endi butun menyu sahifasini shunday qilamiz — kategoriyalar, taom sahifasi, sharhlar." Sardor hisoblab chiqdi: har sahifada uchtadan state, try/catch, abort, "Yuklanmoqda…" va "Xato" matnlari. O'n sahifa — o'ttiz qator takror kod.

Keyin muammolar ko'rindi. Mehmon «Taom» dan «Ichimlik» ga o'tib, qaytdi — menyu yana "Yuklanmoqda…": hozirgina ko'rgan ro'yxat yana serverdan so'raldi. Taom sahifasi avval taomni yuklaydi, keyin sharhlarni — ketma-ket, ikki barobar sekin. Telefonda sahifa ochilganda bir soniya bo'sh oq ekran turadi.

Bu dars — effektdagi fetch ning to'liq shakli va halol bahosi. U qachon yetarli va qachon boshqa vosita kerak.

Hayotiy o'xshatish: oshxonada har buyurtma uchun bozorga borish. Ishlaydi — osh tayyor bo'ladi. Lekin sabzi bugun uchinchi marta kerak bo'lsa ham, har safar bozorga borasiz (kesh yo'q). Go'sht olib kelgandan keyingina guruch uchun yo'lga chiqasiz (sharshara). Restoranlarda buning uchun omborxona va ta'minotchi bor — kodda esa kutubxonalar.

2. To'liq shakl

2.1 Uch holat

Serverdan ma'lumot olayotgan komponent har doim uch holatdan birida: yuklanmoqda, xato yoki tayyor. Ularni bitta status state'ida saqlash qulay: uchta alohida boolean (isLoading, isError…) bir vaqtda true bo'lib qolishi mumkin edi (State strukturasini loyihalash darsidagi "imkonsiz holatlar").

Jonli blok vaqtga va internetga bog'liq bo'lmasligi uchun fetchDish — fetch ning kichik nusxasi: id 99 bo'lsa, server 404 qaytargandek xato beradi.

jsx
import { useEffect, useState } from "react";

const DISHES = { 1: { nom: "Osh", narx: 35000 } };

// fetch o'rnida: 150 ms dan keyin javob; yo'q id — 404 xatosi
function fetchDish(id, signal) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      if (DISHES[id]) resolve(DISHES[id]);
      else reject(new Error(`404: taom ${id} topilmadi`));
    }, 150);
    signal.addEventListener("abort", () => {
      clearTimeout(timer);
      reject(signal.reason);
    });
  });
}

function Dish({ id }) {
  const [status, setStatus] = useState("loading");
  const [dish, setDish] = useState(null);
  const [error, setError] = useState("");
  useEffect(() => {
    const controller = new AbortController();
    setStatus("loading");
    fetchDish(id, controller.signal)
      .then((data) => {
        setDish(data);
        setStatus("ready");
      })
      .catch((err) => {
        if (err.name === "AbortError") return;
        setError(err.message);
        setStatus("error");
      });
    return () => controller.abort();
  }, [id]);
  console.log("chizish:", id, status);
  if (status === "loading") return <p>Yuklanmoqda…</p>;
  if (status === "error") return <p role="alert">Xato: {error}</p>;
  return <p>{dish.nom}: {dish.narx} so'm</p>;
}

function App() {
  const [id, setId] = useState(1);
  return (
    <div>
      <Dish id={id} />
      <button onClick={() => setId(99)}>Taom 99</button>
    </div>
  );
}

Konsolda:

text
chizish: 1 loading
chizish: 1 ready
chizish: 99 ready
chizish: 99 loading
chizish: 99 error

Ekranda:

text
Xato: 404: taom 99 topilmadi
Taom 99

Uch holat ham ko'rindi: avval yuklanish, keyin osh, «Taom 99» dan keyin yana yuklanish va xato. Xato matnida role="alert" — ekran o'quvchi (Malika) uni darhol o'qiydi.

Uchinchi qatorga qarang: chizish: 99 ready. id allaqachon 99, lekin status hali eski — ready, ekranda bir lahza osh turibdi. Effekt chizishdan keyin ishlaydi va setStatus("loading") faqat undan keyin keladi. Bu Sizga effekt kerak emas darsidagi "eskirgan oraliq holat" ning aynan o'zi.

Render Commit Effekt Cleanup
1, loading "Yuklanmoqda…" so'rov 1 —
1, ready osh — —
«Taom 99»: 99, ready osh (eskirgan) so'rov 99 so'rov 1 abort (allaqachon tugagan)
99, loading "Yuklanmoqda…" — —
99, error xato matni — —

Uchinchi qatorda cleanup oldin, keyin yangi effekt ishladi (useEffect III dagi tartib). Uni tuzatish uchun ham qo'shimcha kod kerak (masalan, ota'da key={id}).

2.2 Haqiqiy fetch bilan

fetchDish o'rniga haqiqiy fetch ikki qo'shimcha narsani talab qiladi: res.ok tekshiruvi (fetch 404 da xato tashlamaydi — fetch asoslari) va res.json():

jsx
import { useEffect, useState } from "react";

const API = "https://ilmhamroh.uz/api/mashq/menyu";

async function fetchDish(id, signal) {
  const res = await fetch(`${API}/${id}`, { signal });
  if (!res.ok) throw new Error(`${res.status}: taom ${id} topilmadi`);
  return res.json();
}

function Dish({ id }) {
  const [status, setStatus] = useState("loading");
  const [dish, setDish] = useState(null);
  const [error, setError] = useState("");
  useEffect(() => {
    const controller = new AbortController();
    setStatus("loading");
    fetchDish(id, controller.signal)
      .then((data) => {
        setDish(data);
        setStatus("ready");
      })
      .catch((err) => {
        if (err.name === "AbortError") return;
        setError(err.message);
        setStatus("error");
      });
    return () => controller.abort();
  }, [id]);
  if (status === "loading") return <p>Yuklanmoqda…</p>;
  if (status === "error") return <p role="alert">Xato: {error}</p>;
  return <p>{dish.nom}: {dish.narx} so'm</p>;
}

export default function App() {
  return <Dish id={99} />;
}

«Ishga tushir» bilan sinang: kurs serveri menyu/99 ga 404 qaytaradi va ekranda Xato: 404: taom 99 topilmadi chiqadi. id={1} qilsangiz — Osh: 35000 so'm.

Hisoblaymiz: bitta taomni ko'rsatish uchun 30 qatorga yaqin kod. Undan uchtasi state, to'qqiztasi effekt, uchtasi holatlar bo'yicha return. Har yangi sahifada — yana shuncha.

Tekshirib ko'ring: .catch ichida if (err.name === "AbortError") return; qatorini olib tashlasak, nima bo'ladi? Qachon buni sezamiz?

Javob

id almashganda eski so'rov bekor qilinadi va uning catch i setStatus("error") qiladi. Ekranda bir lahza "Xato: signal is aborted without reason" chiqishi mumkin — yangi so'rovning "Yuklanmoqda…" si o'rnida. StrictMode'da esa bu birinchi ochilishdayoq ko'rinadi: sinov cleanup'i birinchi so'rovni bekor qiladi (Effekt hayot sikli).

3. Kesh yo'q

3.1 Har qaytishda qayta yuklanadi

Kategoriyalar orasida yurib, so'rovlarni sanaymiz. requests — modul o'zgaruvchisi, fetchMenu har chaqirilganda oshadi:

jsx
import { useEffect, useState } from "react";

let requests = 0;
const MENU = {
  taom: ["Osh", "Lag'mon", "Manti"],
  ichimlik: ["Ko'k choy"],
};

function fetchMenu(category) {
  requests++;
  console.log(`so'rov #${requests}: ${category}`);
  return new Promise((resolve) => {
    setTimeout(() => resolve(MENU[category]), 100);
  });
}

function Menu({ category }) {
  const [dishes, setDishes] = useState(null);
  useEffect(() => {
    let ignore = false;
    setDishes(null); // yangi kategoriya — yana "Yuklanmoqda…"
    fetchMenu(category).then((list) => {
      if (!ignore) setDishes(list);
    });
    return () => {
      ignore = true;
    };
  }, [category]);
  if (dishes === null) return <p>Yuklanmoqda…</p>;
  return <p>{dishes.join(", ")}</p>;
}

function App() {
  const [category, setCategory] = useState("taom");
  return (
    <div>
      <Menu category={category} />
      <button onClick={() => setCategory("taom")}>Taom</button>{" "}
      <button onClick={() => setCategory("ichimlik")}>
        Ichimlik
      </button>
    </div>
  );
}

Konsolda:

text
so'rov #1: taom
so'rov #2: ichimlik
so'rov #3: taom

Ekranda:

text
Osh, Lag'mon, Manti
Taom Ichimlik

Taomlar ikki marta so'raldi. Ikkinchi marta ham mehmon "Yuklanmoqda…" ni ko'rdi, garchi ro'yxat bir necha soniya oldin ekranda bo'lgan bo'lsa ham. Komponentning state'i u bilan birga yo'qoladi yoki almashadi — eslab qolish uchun joy yo'q.

Kesh (cache) — oldin olingan javobni saqlab, keyingi safar darhol berish (Kesh strategiyalari brauzer va Service Worker darajasida bor, lekin React state'i undan bexabar). Kesh qo'shish — qo'lda yozilsa — yangi savollar tug'diradi. Qachon eskiradi? Server ma'lumoti o'zgarsa-chi? Ikki komponent bir xil narsani so'rasa — bitta so'rov bo'lsinmi? Bu "server holati" ning alohida dunyosi (Server state va client state).

3.2 Bir xil ma'lumot — ikki so'rov

Sahifa tepasidagi "Bugun 4 ta taom" hisoblagichi va pastdagi menyu ro'yxati bir xil ma'lumotga muhtoj. Har biri o'z effektida fetch qilsa — ikki bir xil so'rov. State'ni ota'ga ko'tarish (State'ni ko'tarish) yordam beradi, lekin komponentlar daraxtning turli chekkalarida bo'lsa, ma'lumotni pastga uzun props zanjiri bilan uzatish kerak bo'ladi.

4. Sharshara (waterfall)

4.1 Ota yuklaydi, keyin bola

App menyuni oladi, keyin birinchi taomni Dish ga beradi. Dish o'z effektida taom tafsilotini so'raydi. Lekin Dish faqat menyu kelgandan keyin chiziladi — demak uning effekti ham keyin boshlanadi. Kurs serverida har javob 500 ms ga sekinlashtirilgan (?kechikish=500):

jsx
import { useEffect, useState } from "react";

const API = "https://ilmhamroh.uz/api/mashq/menyu";
const start = performance.now();
const ms = () => Math.round(performance.now() - start);

function Dish({ id }) {
  const [dish, setDish] = useState(null);
  useEffect(() => {
    console.log(`${ms()} ms: so'rov — taom ${id}`);
    fetch(`${API}/${id}?kechikish=500`)
      .then((res) => res.json())
      .then((data) => {
        console.log(`${ms()} ms: javob — taom ${id}`);
        setDish(data);
      });
  }, [id]);
  if (dish === null) return <p>Yuklanmoqda…</p>;
  return <p>{dish.nom}: {dish.narx} so'm</p>;
}

function App() {
  const [menu, setMenu] = useState(null);
  useEffect(() => {
    console.log(`${ms()} ms: so'rov — menyu`);
    fetch(`${API}?kechikish=500`)
      .then((res) => res.json())
      .then((data) => {
        console.log(`${ms()} ms: javob — menyu`);
        setMenu(data);
      });
  }, []);
  if (menu === null) return <p>Menyu yuklanmoqda…</p>;
  return <Dish id={menu[0].id} />;
}

Chrome 154 da uch marta o'lchadik (kod soddalik uchun abort'siz):

text
19 ms: so'rov — menyu
557 ms: javob — menyu
560 ms: so'rov — taom 1
1094 ms: javob — taom 1

Qolgan ikki o'lchov: 1 087 va 1 092 ms. Ikkinchi so'rov birinchisi tugaguncha boshlanmadi — Dish hali yo'q edi. Sharshara (waterfall) — so'rovlar ketma-ket, har biri oldingisini kutadi. DevTools'ning Network panelida ular zinapoya kabi pastga tushadi — nomi shundan.

gantt
  title So'rovlar vaqt bo'yicha (ms)
  dateFormat x
  axisFormat %L
  section Sharshara
  menyu      :0, 557
  taom 1     :560, 1094
  section Parallel
  menyu      :0, 564
  taom 1     :0, 564

Diagrammaning yuqori qismi — sharshara: ikkinchi chiziq birinchisi tugagach boshlanadi. Pastki qism — keyingi bo'limdagi parallel usul.

4.2 Parallel: ikkalasi birga

Agar taom id si oldindan ma'lum bo'lsa (masalan, URL'dan: /menyu/1), ikkala so'rovni bitta effektda birga boshlash mumkin — Promise.all (Promise kombinatorlari):

jsx
import { useEffect, useState } from "react";

const API = "https://ilmhamroh.uz/api/mashq/menyu";
const start = performance.now();
const ms = () => Math.round(performance.now() - start);
const getJSON = (url) => fetch(url).then((res) => res.json());

function App() {
  const [data, setData] = useState(null);
  useEffect(() => {
    console.log(`${ms()} ms: so'rov — menyu va taom 1 birga`);
    Promise.all([
      getJSON(`${API}?kechikish=500`),
      getJSON(`${API}/1?kechikish=500`),
    ]).then(([menu, dish]) => {
      console.log(`${ms()} ms: ikkala javob`);
      setData({ menu, dish });
    });
  }, []);
  if (data === null) return <p>Yuklanmoqda…</p>;
  return <p>{data.dish.nom}: {data.dish.narx} so'm</p>;
}
text
17 ms: so'rov — menyu va taom 1 birga
564 ms: ikkala javob

Uch o'lchov: 564, 564, 569 ms — sharsharadan deyarli ikki barobar tez. Lekin narxi bor: endi ma'lumot olish bola komponentdan ota'ga ko'chdi. Bola "nimaga muhtojman" ni o'zi aytmaydi — ota hammasini oldindan bilishi kerak. Komponentlar ko'payganda bu tez chalkashadi.

Framework'lar sharsharani boshqacha yechadi: ma'lumot komponent chizilishidan oldin, sahifa (marshrut) darajasida so'raladi. Masalan, router "loader" funksiyalari (React Router IV: data mode) va server komponentlari (Server Components).

5. Yana uchta cheklov

Cheklov Nima bo'ladi Batafsil
StrictMode (dev) har so'rov dev'da ikki marta yuboriladi (biri bekor) Effekt hayot sikli
Server'da ishlamaydi effekt faqat brauzerda — HTML bo'sh keladi, ma'lumot JS yuklangach Next.js qismida
Takror kod har komponentda uch state, abort, xato Custom hooklar qisman yechadi

Ikkinchi qator haqida. Server'da chiziladigan sahifa (SSR) uchun React komponentni serverda chaqiradi, lekin effektlarni ishlatmaydi — effekt "brauzer ekranni chizgandan keyin" degani, serverda esa ekran yo'q. Demak useEffect dagi fetch bilan qurilgan sahifa serverdan "Yuklanmoqda…" holatida keladi. Google uni shunday ko'radi, mehmon esa JavaScript yuklanguncha bo'sh sahifani ko'radi.

6. Unda nima ishlatiladi

Bu kursda keyingi qismlarda ma'lumot olishning to'rt yo'lini o'rganamiz. Hozir ularning nomini va qaysi muammoni hal qilishini bilish yetarli — kodini yozish shart emas:

Vosita Nima hal qiladi Qayerda
TanStack Query (yoki SWR) — kutubxona kesh, takror so'rovni birlashtirish, poyga, qayta urinish, eskirganini fonda yangilash TanStack Query I
Router "loader" sharshara: ma'lumot sahifa ochilishidan oldin, parallel React Router IV
use() + Suspense "Yuklanmoqda…" ni deklarativ ko'rsatish Suspense, use()
Server komponentlar (Next.js) ma'lumot serverda, HTML tayyor keladi Server Components

Ularning hammasi ichida bugungi darsdagi narsalar bor: AbortController, holatlar, xatolar. Farqi — siz ularni har komponentda qayta yozmaysiz.

Maslahat: React hujjati ham shuni tavsiya qiladi: framework ishlatsangiz — uning ma'lumot olish usulini; ishlatmasangiz — kesh qiladigan kutubxonani. Effektdagi fetch ni o'zingiz yozsangiz — bugungi «To'liq shakl» dagi hamma narsani unutmang.

6.1 Effektdagi fetch qachon yetarli

Halol bo'laylik: har doim ham kutubxona kerak emas.

  • O'rganish va prototip — mexanizmni tushunish uchun bugungi kod eng yaxshi maktab.
  • Bitta joyda, bitta so'rov — kesh ham, parallellik ham kerak bo'lmagan sahifa.
  • Tashqi tizim bilan sinxronlash — vazifalar dagi server sinxroni aynan shunday: u "ma'lumot ko'rsatish" emas, "ikki nusxani moslashtirish". Bitta ro'yxat, bitta so'rov, AbortController bilan.

7. Vazifalar kuzatuvi

Bu dars vazifalar kodini o'zgartirmaydi. Kanondagi server effekti (useEffect III) — effektda ma'lumot olish. Uni bugungi ro'yxat bo'yicha tekshiramiz:

Muammo vazifalar da Nega
poyga yo'q AbortController — sinxronniTox
uch holat bor tarmoq: yuklanmoqda / xato / oflayn / tayyor
kesh kerak emas ro'yxat localStorage da, ekranda darhol
sharshara yo'q bitta so'rov (kalit bo'lmasa — keyin PUT)
StrictMode takrori bor, zararsiz birinchi GET bekor qilinadi
server'da chizish kerak emas SPA, statik hosting

Qatorlarning ko'pi "yo'q" yoki "kerak emas" — kichik ilova uchun effekt yetarli. Uchinchi qatorga qarang: vazifalar "keshi" — localStorage. Sahifa ochilganda ro'yxat darhol ko'rinadi, server esa fonda solishtiriladi. Bu usulning nomi — "stale-while-revalidate" (eskisini ko'rsat, fonda yangila). TanStack Query'ning asosiy g'oyasi ham shu. TanStack Query qismida server holatini to'liq o'rganamiz.

8. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
404 da "tayyor", ichida undefined fetch 404 da tashlamaydi if (!res.ok) throw …
isLoading va isError birga true uchta boolean bitta status
almashganda bir lahza eski ma'lumot effekt chizishdan keyin ota'da key, yoki kutubxona
Xato: signal is aborted ekranda AbortError ajratilmagan if (err.name === "AbortError") return;
ikkinchi so'rov birinchisini kutadi sharshara parallel (Promise.all) yoki loader
qaytganda yana "Yuklanmoqda…" kesh yo'q kutubxona yoki yuqoriroqda saqlash

9. Mashqlar

Mashqlar kurs/mashqlar/17/32-effektda-malumot/ da. Har yechimni «Ishga tushir» bilan sinang.

1-mashq (oson): muammoni nomlang

Har tavsifga muammo nomini yozing: poyga, sharshara yoki kesh.

  • Taom sahifasi avval taomni, u kelgach — sharhlarni so'raydi:
  • «Orqaga» bosilganda hozirgina ko'rilgan ro'yxat yana yuklanadi:
  • Tez almashtirilganda eski kategoriyaning javobi yangisining ustiga yoziladi:
Yechim

Sharshara — so'rovlar ketma-ket, bittasi ikkinchisini kutadi. Kesh yo'qligi — olingan javob saqlanmaydi. Poyga — javoblar so'rovlar tartibida kelmaydi (useEffect III).

2-mashq (o'rta): xato holati

Reviews komponenti taom sharhlarini oladi, lekin faqat "tayyor" holatini biladi. Server xato bersa (bu blokda fetchReviews doim xato beradi), ekran abadiy "Yuklanmoqda…" da qoladi. Kodga status va error state'larini qo'shing: xatoda role="alert" li <p> da Sharhlar yuklanmadi: … chiqsin. Ishora: «Uch holat» bo'limidagi Dish.

jsx
import { useEffect, useState } from "react";

function fetchReviews(dishId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error("500: server xatosi")), 100);
  });
}

function Reviews({ dishId }) {
  const [reviews, setReviews] = useState(null);
  useEffect(() => {
    let ignore = false;
    fetchReviews(dishId).then((list) => {
      if (!ignore) setReviews(list);
    });
    return () => {
      ignore = true;
    };
  }, [dishId]);
  if (reviews === null) return <p>Yuklanmoqda…</p>;
  return <p>{reviews.length} ta sharh</p>;
}

function App() {
  return <Reviews dishId={1} />;
}

Sayt oynasida (Chrome 154 da tekshirildi):

text
Konsol: Uncaught (in promise) Error: 500: server xatosi
Ekran:  Yuklanmoqda…
Yechim
jsx
import { useEffect, useState } from "react";

function fetchReviews(dishId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error("500: server xatosi")), 100);
  });
}

function Reviews({ dishId }) {
  const [status, setStatus] = useState("loading");
  const [reviews, setReviews] = useState([]);
  const [error, setError] = useState("");
  useEffect(() => {
    let ignore = false;
    setStatus("loading");
    fetchReviews(dishId)
      .then((list) => {
        if (ignore) return;
        setReviews(list);
        setStatus("ready");
      })
      .catch((err) => {
        if (ignore) return;
        setError(err.message);
        setStatus("error");
      });
    return () => {
      ignore = true;
    };
  }, [dishId]);
  if (status === "loading") return <p>Yuklanmoqda…</p>;
  if (status === "error") {
    return <p role="alert">Sharhlar yuklanmadi: {error}</p>;
  }
  return <p>{reviews.length} ta sharh</p>;
}

function App() {
  return <Reviews dishId={1} />;
}

Ekranda:

text
Sharhlar yuklanmadi: 500: server xatosi

Konsol toza: xato catch da ushlandi. Birinchi variantdagi Uncaught (in promise) — ushlanmagan rad etilgan Promise (async/await). Effektdagi har fetch zanjiri .catch bilan tugashi shart.

3-mashq (qiyin): sharsharani yo'qoting

App menyuni, Dish esa taomni oladi — sharshara. Konsoldagi "qadam" lar so'rovlar tartibini ko'rsatadi. Kodni o'zgartiring: ikkala so'rov bitta effektda, Promise.all bilan birga boshlansin (taom id si — 1, oldindan ma'lum). Dish endi ma'lumotni props'dan olsin va o'zi so'rov yubormasin. Konsolda ikki so'rov ketma-ket boshlanib, keyin javoblar kelsin.

jsx
import { useEffect, useState } from "react";

function fakeFetch(name, data) {
  console.log("so'rov:", name);
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("javob:", name);
      resolve(data);
    }, 100);
  });
}

function Dish({ id }) {
  const [dish, setDish] = useState(null);
  useEffect(() => {
    fakeFetch(`taom ${id}`, { nom: "Osh" }).then(setDish);
  }, [id]);
  return <p>{dish ? dish.nom : "…"}</p>;
}

function App() {
  const [menu, setMenu] = useState(null);
  useEffect(() => {
    fakeFetch("menyu", [{ id: 1 }, { id: 4 }]).then(setMenu);
  }, []);
  if (menu === null) return <p>Menyu…</p>;
  return <Dish id={menu[0].id} />;
}

Konsolda:

text
so'rov: menyu
javob: menyu
so'rov: taom 1
javob: taom 1
Yechim
jsx
import { useEffect, useState } from "react";

function fakeFetch(name, data) {
  console.log("so'rov:", name);
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log("javob:", name);
      resolve(data);
    }, 100);
  });
}

function Dish({ dish }) {
  return <p>{dish.nom}</p>;
}

function App() {
  const [data, setData] = useState(null);
  useEffect(() => {
    let ignore = false;
    Promise.all([
      fakeFetch("menyu", [{ id: 1 }, { id: 4 }]),
      fakeFetch("taom 1", { nom: "Osh" }),
    ]).then(([menu, dish]) => {
      if (!ignore) setData({ menu, dish });
    });
    return () => {
      ignore = true;
    };
  }, []);
  if (data === null) return <p>Menyu…</p>;
  return <Dish dish={data.dish} />;
}

Konsolda:

text
so'rov: menyu
so'rov: taom 1
javob: menyu
javob: taom 1

Ekranda:

text
Osh

Ikkala so'rov birga boshlandi: umumiy vaqt — eng sekin javobniki (bu yerda 100 ms), yig'indisi emas (200 ms). Dish endi oddiy, "toza" komponent — faqat props. Lekin endi App Dish qanday ma'lumotga muhtojligini bilishi kerak. Katta ilovada bu bog'liqlikni router loader'lari yoki server komponentlar boshqaradi.

10. Real ishda

  • Ko'p ishlab chiqarish (production) loyihalarida useEffect ichida to'g'ridan-to'g'ri fetch deyarli uchramaydi: TanStack Query yoki framework'ning ma'lumot olish usuli ishlatiladi. Lekin intervyuda va eski kodda bu naqsh har doim bor — uni o'qiy olish va kamchiliklarini aytib berish majburiy.
  • Network panelidagi sharshara — sahifa sekinligining birinchi sababi. Lighthouse va Web Vitals'dagi LCP (eng katta element qachon chizildi) ko'pincha shundan yomonlashadi.
  • res.ok tekshiruvi va AbortError ni ajratish — kutubxona ishlatsangiz ham kerak bo'ladigan odatlar (API klient qatlami).
  • Intervyu savollari: "useEffect'da fetch qilishning kamchiliklari?", "waterfall nima va qanday oldini olasiz?", "nega TanStack Query kerak, oddiy fetch yetmaydimi?".

Xulosa

  • To'liq shakl: bitta status (loading / error / ready), res.ok tekshiruvi, .catch, AbortController va AbortError ni ajratish.
  • Kesh yo'q: qaytishda qayta yuklanadi, bir xil ma'lumot — bir necha so'rov.
  • Sharshara: bola so'rovi ota javobini kutadi. O'lchov: 1,09 s o'rniga parallel 0,56 s (Promise.all).
  • Effekt serverda ishlamaydi, StrictMode dev'da ikki so'rov yuboradi, har komponentda takror kod.
  • Real ilovada — kutubxona (TanStack Query, SWR) yoki framework (router loader, server komponentlar); effektdagi fetch — o'rganish, kichik ilova va sinxronlash uchun.

Keyingi dars: useRef I: render chaqirmaydigan xotira — o'zgarganda qayta chizilmaydigan quti: taymer id, oldingi qiymat va ref bilan state farqi.

Manbalar

  • React: "Synchronizing with Effects" — "What are good alternatives to data fetching in Effects?" — react.dev/learn/synchronizing-with-effects
  • React: "You Might Not Need an Effect" — "Fetching data" — react.dev/learn/you-might-not-need-an-effect
  • MDN: Response.ok, AbortController — developer.mozilla.org/docs/Web/API
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Effektda ma'lumot olish: fetch + useEffect to'liq shakli va uning cheklovlari — IlmHamroh