IlmHamroh
JavaScript Full-stack/17-qism. React asoslari27/45-dars20 daqiqa
Mundarija (25)

useEffect II: dependency massivi chuqur — Object.is, obyekt va funksiya bog'liqliklari

Qisqacha: Har chizishdan keyin React bog'liqliklar massividagi har qiymatni oldingi chizishdagisi bilan Object.is orqali solishtiradi. Birortasi farq qilsa — effekt qayta ishlaydi. Son va satr qiymati bo'yicha solishtiriladi. Komponent ichida yaratilgan obyekt, massiv va funksiya esa har chizishda yangi, shuning uchun ular massivda tursa, effekt har safar ishlaydi. Davosi: massivga primitiv maydonni yozish yoki obyektni effekt ichida yaratish. Massiv — sizning tanlovingiz emas, effekt ishlatgan reaktiv qiymatlar ro'yxati. ESLint exhaustive-deps uni tekshiradi; massivdan qiymatni olib tashlab "aldash" effektni eskirgan qiymat bilan qoldiradi.

Bu darsda

  • Massivning uch shaklini konsolda taqqoslaysiz va qaysi biri qachon kerakligini aytasiz.
  • React massivni Object.is bilan qanday solishtirishini tushuntira olasiz.
  • Obyekt va funksiya bog'liqligi nega effektni har chizishda ishga tushirishini ko'rasiz va uch usulda tuzatasiz.
  • ESLint exhaustive-deps xabarlarini o'qiysiz va ularga ko'r-ko'rona emas, o'ylab javob berasiz.
  • Massivni "aldash" nima bilan tugashini jonli ko'rasiz.

Oldin bilishingiz kerak: useEffect I, Tenglik: ==, === va Object.is, Closure tuzoqlari, State II: snapshot va updater funksiya.

1. Nega bu kerak?

Oldingi darsda effektni yozdik va massivga "effekt ishlatgan qiymatlar" ni qo'yishni o'rgandik. Bir qarashda oddiy qoida. Lekin Sardor ikki kunda ikki xil muammoga duch keldi.

Birinchi kuni u menyu filtrini obyektga yig'di: const filter = { category, limit: 10 } va [filter] deb yozdi. Konsolga qarasa — effekt har chizishda ishlayapti, hech narsa o'zgarmasa ham. Hatto mutlaqo boshqa tugmani bosganda ham.

Ikkinchi kuni u "ortiqcha ishlamasin" deb massivni bo'shatdi: []. Endi effekt bir marta ishladi — va kategoriya almashganda tab nomi eski qoldi. ESLint sariq chiziq chizdi, Sardor uni // eslint-disable-next-line bilan o'chirdi.

Ikkala muammoning ildizi bitta: massiv qanday ishlashini aniq bilmaslik. Bugun shu "qora quti" ni ochamiz. Hayotiy o'xshatish: oshxonada buyurtma qog'ozi. Oshpaz yangi taom tayyorlashdan oldin qog'ozdagi har qatorni avvalgisi bilan solishtiradi. Bitta qator farq qilsa — qaytadan pishiradi. Lekin ofitsiant har safar xuddi shu buyurtmani yangi qog'ozga qayta yozib kelsa, va oshpaz qog'ozning o'ziga qarasa (matniga emas) — u har safar "yangi buyurtma" deb o'ylaydi.

2. Uch shakl — yonma-yon

Bitta komponentda uchta effekt: massivsiz, bo'sh massivli va [category] li. Tugmalar kategoriyani tanlaydi va bosishlar sonini oshiradi. Blok uch marta bosadi: «Taom» (o'sha kategoriya), «Ichimlik», yana «Ichimlik».

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

function App() {
  const [category, setCategory] = useState("taom");
  const [clicks, setClicks] = useState(0);
  useEffect(() => {
    console.log("A (massivsiz):", category);
  });
  useEffect(() => {
    console.log("B ([]):", category);
  }, []);
  useEffect(() => {
    console.log("C ([category]):", category);
  }, [category]);
  function choose(next) {
    setCategory(next);
    setClicks(clicks + 1);
  }
  return (
    <div>
      <p>{category}, bosildi: {clicks}</p>
      <button onClick={() => choose("taom")}>Taom</button>{" "}
      <button onClick={() => choose("ichimlik")}>Ichimlik</button>
    </div>
  );
}

Konsolda:

text
A (massivsiz): taom
B ([]): taom
C ([category]): taom
A (massivsiz): taom
A (massivsiz): ichimlik
C ([category]): ichimlik
A (massivsiz): ichimlik

Ekranda:

text
ichimlik, bosildi: 3
Taom Ichimlik

To'rt chizish bo'ldi: ochilish va uch bosish. Har birida qaysi effekt ishladi:

Chizish (category) A (massivsiz) B ([]) C ([category])
ochilish (taom)
«Taom» (taom) — — (o'sha)
«Ichimlik» (ichimlik) —
«Ichimlik» (ichimlik) — — (o'sha)
  • A — har chizishda. clicks o'zgargani ham yetarli edi.
  • B — faqat birinchi chizishda. Uning category si abadiy "taom" bo'lib qoldi.
  • C — category haqiqatan o'zgargandagina.

Effekt ham bir xil sondagi chizishlardan keyin bajariladi — farq faqat massivda. React effektlarni yozilgan tartibda ishlatadi: A, B, C.

Tekshirib ko'ring: Jadvalning ikkinchi qatorida «Taom» bosildi, lekin category o'zgarmadi. Unda nega chizish bo'ldi va A ishladi?

Javob

Chunki clicks o'zgardi (0 → 1). Bitta ishlovchida ikkita set… chaqirildi — React ularni bitta chizishga birlashtirdi (batching). Chizish bo'ldi — massivsiz A ishladi. C esa category ni solishtirdi: "taom" va "taom" — bir xil, o'tkazib yuborildi.

3. React qanday solishtiradi

3.1 Object.is — har element alohida

Massivni React butunligicha solishtirmaydi. U har elementni oldingi chizishdagi o'sha o'rindagi element bilan Object.is orqali taqqoslaydi (Tenglik darsidan tanish). Hech bo'lmasa bittasi false bersa — effekt ishlaydi.

flowchart TD
  S["Chizish tugadi, commit bo'ldi"] --> M{"Massiv bormi?"}
  M -- "yo'q" --> RUN["Effekt ishlaydi"]
  M -- "ha" --> F{"Birinchi chizishmi?"}
  F -- "ha" --> RUN
  F -- "yo'q" --> C{"Har element: Object.is(eski, yangi)"}
  C -- "biri false" --> RUN
  C -- "hammasi true" --> SKIP["O'tkazib yuboriladi"]

Diagrammadagi pastki romb — butun sirning kaliti. Object.is qanday javob berishini eslaymiz:

js
console.log(Object.is("taom", "taom")); // true
console.log(Object.is(35000, 35000)); // true
console.log(Object.is({ dish: "osh" }, { dish: "osh" })); // false
const filter = { category: "taom" };
console.log(Object.is(filter, filter)); // true
console.log(Object.is(() => 1, () => 1)); // false
console.log(Object.is(NaN, NaN), NaN === NaN); // true false

Son va satr — qiymati bo'yicha. Obyekt, massiv va funksiya — havolasi bo'yicha: ichi bir xil ikki obyekt — baribir ikki xil obyekt. Bitta obyektning o'zi — o'ziga teng. NaN — Object.is uchun o'ziga teng (=== uchun emas). Shu sababli React === emas, aynan Object.is ni tanlagan: NaN li bog'liqlik har chizishda "o'zgardi" deyilmaydi.

3.2 Komponent ichidagi obyekt — har chizishda yangi

Endi Sardorning birinchi muammosi. Filtr obyekti komponent ichida yaratiladi. Blok +1 ni ikki marta bosadi — kategoriya o'zgarmaydi:

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

function App() {
  const [category] = useState("taom");
  const [n, setN] = useState(0);
  const filter = { category, limit: 10 }; // har chizishda YANGI
  useEffect(() => {
    console.log("effekt: filtr", filter.category);
  }, [filter]);
  return <button onClick={() => setN(n + 1)}>+1</button>;
}

Konsolda:

text
effekt: filtr taom
effekt: filtr taom
effekt: filtr taom

Uch chizish — uch effekt, garchi category hech qachon o'zgarmagan bo'lsa ham. Sabab: komponent har chizishda boshidan chaqiriladi va const filter = { … } qatori har safar yangi obyekt yaratadi. React Object.is(eskiFilter, yangiFilter) ni tekshiradi — false. U uchun bu "yangi qog'ozdagi buyurtma".

Bu yerda effekt faqat konsolga yozyapti — zarari yo'q. Lekin effekt serverga so'rov yuborsa yoki chat'ga ulansa, har bosishda yangi so'rov va qayta ulanish. Agar effekt ichida state o'zgarsa — cheksiz sikl.

3.3 Uchta davo

1. Primitiv maydonni massivga yozing. Effektga butun obyekt emas, uning category maydoni kerak edi:

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

function App() {
  const [category] = useState("taom");
  const [n, setN] = useState(0);
  const filter = { category, limit: 10 };
  const filterCategory = filter.category; // satr — qiymati bilan
  useEffect(() => {
    console.log("effekt: filtr", filterCategory);
  }, [filterCategory]);
  return <button onClick={() => setN(n + 1)}>+1</button>;
}

Konsolda:

text
effekt: filtr taom

2. Obyektni effekt ichida yarating. Obyekt faqat effektga kerak bo'lsa, uni effektning o'zida yasang — massivda faqat uning "xom ashyosi" qoladi:

jsx
useEffect(() => {
  const filter = { category, limit: 10 }; // effekt ichida
  console.log("effekt: filtr", filter.category);
}, [category]);

3. O'zgarmas narsani komponentdan tashqariga chiqaring. limit: 10 hech qachon o'zgarmaydi — u komponentning har chizishiga bog'liq emas. Modul darajasidagi const LIMIT = 10 — har doim o'sha qiymat, massivga yozish ham shart emas.

Maslahat: Obyektni har chizishda bir xil havola bilan saqlab turuvchi useMemo hooki ham bor (memo, useMemo, useCallback darsida). Lekin u effekt muammosining birinchi davosi emas. Avval yuqoridagi uch usulni sinang — ular sodda va ko'pincha yetarli.

4. Funksiya bog'liqligi

4.1 Funksiya ham obyekt

Funksiya ham JavaScript'da obyekt (Funksiya — qiymat). Komponent ichida e'lon qilingan funksiya har chizishda yangidan yaratiladi:

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

function App() {
  const [category] = useState("taom");
  const [n, setN] = useState(0);
  function buildTitle() {
    return `Bahor — ${category}`;
  }
  useEffect(() => {
    document.title = buildTitle();
    console.log("sarlavha:", document.title);
  }, [buildTitle]);
  return <button onClick={() => setN(n + 1)}>+1</button>;
}

Konsolda:

text
sarlavha: Bahor — taom
sarlavha: Bahor — taom
sarlavha: Bahor — taom

Xuddi obyekt kabi: uch chizish — uch effekt. buildTitle har safar yangi funksiya.

4.2 Davolar

Uchala davo funksiyaga ham to'g'ri keladi:

  • Effekt ichiga ko'chiring. Funksiya faqat effektda ishlatilsa — u effektning ichida yashasin. Massivda esa u o'qiydigan qiymat (category) qoladi.
  • Komponentdan tashqariga chiqaring. Funksiya props va state'ni ishlatmasa (formatPrice(n) kabi), uni komponentdan tashqarida e'lon qiling — u bitta, o'zgarmas.
  • useCallback — funksiyani chizishlar orasida saqlab turuvchi hook (memo, useMemo, useCallback darsida).

Birinchi davo bilan:

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

function App() {
  const [category] = useState("taom");
  const [n, setN] = useState(0);
  useEffect(() => {
    function buildTitle() {
      return `Bahor — ${category}`;
    }
    document.title = buildTitle();
    console.log("sarlavha:", document.title);
  }, [category]);
  return <button onClick={() => setN(n + 1)}>+1</button>;
}

Konsolda:

text
sarlavha: Bahor — taom

4.3 Barqaror qiymatlar — massivga shart emas

Ba'zi qiymatlar komponent ichida bo'lsa ham, React ularni har chizishda bir xil qilib beradi:

  • useState dan qaytgan set funksiyasi (setCount) — doim o'sha funksiya;
  • useRef qaytargan obyekt (useRef I darsida).

ESLint ularni taniydi va massivga yozishni talab qilmaydi. Yozsangiz ham zarari yo'q — ular hech qachon "o'zgarmaydi".

Reaktiv qiymat (reactive value) — chizishdan chizishga o'zgarishi mumkin bo'lgan qiymat: props, state va komponent ichida ulardan hisoblangan har narsa (obyekt va funksiyalar ham). Massiv — aynan effekt ishlatgan reaktiv qiymatlar ro'yxati. Komponentdan tashqaridagi const va barqaror set funksiyalari reaktiv emas.

Tekshirib ko'ring: const LIMIT = 10; komponentdan tashqarida. Effekt ichida LIMIT ishlatilsa, uni massivga yozish kerakmi? const limit = 10; komponent ichida bo'lsa-chi?

Javob

Tashqaridagi LIMIT — yo'q: u modul yuklanganda bir marta yaratiladi va hech qachon o'zgarmaydi. ESLint ham so'ramaydi. Ichidagi limit = 10 — rasman reaktiv qiymat (komponent ichida), lekin son har safar 10. Object.is(10, 10) — true, massivda tursa ham effektni qayta ishga tushirmaydi. Toza yo'l — uni tashqariga chiqarish: o'zgarmas narsa komponentga tegishli emas.

5. ESLint exhaustive-deps

5.1 Qoida nimani tekshiradi

eslint-plugin-react-hooks ning ikkinchi qoidasi — exhaustive-deps ("to'liq bog'liqliklar"). U effekt ichida ishlatilgan har reaktiv qiymatni topadi va massivda bor-yo'qligini tekshiradi. Sardorning ikki kunlik xatolarini bitta faylga yig'dik:

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

export function MenuTitle({ category }) {
  const [count, setCount] = useState(0);
  const filter = { category, limit: 10 };
  function buildTitle() {
    return `Bahor — ${category}`;
  }
  useEffect(() => {
    document.title = buildTitle();
  }, [buildTitle]);
  useEffect(() => {
    console.log("filtr:", filter.category);
  }, [filter]);
  useEffect(() => {
    document.title = `${category}: ${count}`;
  }, []);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

ESLint 10.12.0 va plagin 7.1.1 (qoida nomi qisqartirildi):

text
   5:9  warning  The 'filter' object makes the dependencies of useEffect Hook (at line 14) change on every render. Move it inside the useEffect callback. Alternatively, wrap the initialization of 'filter' in its own useMemo() Hook
   6:3  warning  The 'buildTitle' function makes the dependencies of useEffect Hook (at line 11) change on every render. Move it inside the useEffect callback. Alternatively, wrap the definition of 'buildTitle' in its own useCallback() Hook
  17:6  warning  React Hook useEffect has missing dependencies: 'category' and 'count'. Either include them or remove the dependency array

✖ 3 problems (0 errors, 3 warnings)

Tarjimasi:

  • 5-qator: "filter obyekti useEffect hookining (14-qator) bog'liqliklarini har chizishda o'zgartiradi. Uni useEffect ichiga ko'chiring yoki useMemo ga o'rang." Aynan «Uchta davo» bo'limidagi ikkinchi davo.
  • 6-qator: xuddi shunday, buildTitle funksiyasi uchun; muqobili — useCallback.
  • 17-qator: "useEffect da bog'liqliklar yetishmayapti: category va count. Ularni qo'shing yoki massivni olib tashlang."

E'tibor bering: hammasi warning (sariq), error emas. Kod ishlaydi — faqat noto'g'ri ishlaydi.

5.2 Massivni "aldash" — eskirgan closure

Uchinchi xabar Sardorning ikkinchi kuni. U [] yozdi, chunki effekt "bir marta ishlasin" demoqchi edi. Natija jonli: «Ichimlik» bosiladi, lekin tab nomi qotib qoladi. Ikkinchi effekt (massivsiz) har chizishdan keyin hozirgi tab nomini konsolga yozadi:

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

function App() {
  const [category, setCategory] = useState("taom");
  useEffect(() => {
    document.title = `Bahor — ${category}`;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []); // ❌ "aldash": category ishlatiladi, lekin massivda yo'q
  useEffect(() => {
    console.log(`ekranda: ${category}, tabda: ${document.title}`);
  });
  return (
    <button onClick={() => setCategory("ichimlik")}>Ichimlik</button>
  );
}

Konsolda:

text
ekranda: taom, tabda: Bahor — taom
ekranda: ichimlik, tabda: Bahor — taom

Ekranda — ichimlik, tabda — hali ham taom. Bosqichlar bo'yicha:

Render Commit Effekt Cleanup
ochilish: taom <button> DOM'ga 1-effekt: tab = taom; 2-effekt: log —
«Ichimlik»: ichimlik matn o'sha 1-effekt o'tkazildi ([]); 2-effekt: log —

Jadvalning ikkinchi qatori — muammoning o'zi: render yangi, 1-effekt esa massivi bo'sh bo'lgani uchun umuman ishga tushmadi. Effekt birinchi chizishning category sini closure orqali ushlab qoldi va boshqa hech qachon ishlamadi (Closure tuzoqlari — eskirgan closure (stale closure)). Hech qanday xato yo'q, konsol toza — ESLint ogohlantirishi esa izoh bilan o'chirilgan. Bunday xatoni production'da mijoz topadi.

Massivni aldash — effekt ishlatgan reaktiv qiymatni massivdan ataylab chiqarish. U "effekt kamroq ishlasin" degan istakdan tug'iladi. Lekin massiv istak emas, fakt: "effekt shu qiymatlarga bog'liq". Effekt kamroq ishlashini istasangiz, massivni emas, kodni o'zgartiring — effekt kamroq qiymatga bog'liq bo'lsin.

5.3 Bog'liqlikni halol kamaytirish

Massivni qisqartirishning halol yo'llari:

Holat Yo'l
obyekt yoki funksiya faqat effektda kerak effekt ichiga ko'chiring
props/state ishlatmaydigan yordamchi komponentdan tashqariga
setCount(count + 1) — count faqat yangilash uchun updater: setCount((c) => c + 1) (State II)
eng so'nggi qiymatni o'qish kerak, lekin qayta ishga tushish kerak emas useEffectEvent (useEffectEvent darsida)
hisoblanadigan qiymat effektda effekt umuman kerak emas (Sizga effekt kerak emas)

Uchinchi qatordagi updater keyingi darsdagi taymerda kerak bo'ladi: setInterval ichida setSeconds((s) => s + 1) — seconds massivga kirmaydi, taymer har soniyada qayta yaratilmaydi.

Diqqat: // eslint-disable-next-line react-hooks/exhaustive-deps — React kodida eng xavfli izohlardan biri. Uni ko'rsangiz, kod ko'rigida birinchi savol: "qaysi qiymat massivdan chiqarilgan va effekt uning eskirgan nusxasi bilan ishlasa nima bo'ladi?"

6. Vazifalar qadami: kanondagi massivlar

refactor/effektlar branch'ida ishni davom ettiramiz. Eslatma: kanonda #26–#28 qadamlari bitta commit — biz uni uch darsga bo'lib o'qiyapmiz, commit esa useEffect III darsi oxirida. Bugun yangi fayl yo'q — kanondagi oltita effektning massivini bittalab o'qiymiz (to'liq kod — useEffect III darsida, cleanup bilan):

Effekt Massiv Nega aynan shunday
localStorage (saqla) [vazifalar] ro'yxat o'zgarsa — saqlash
boshqa tablar (tingla) [] obuna bir marta; ichida faqat set funksiyalari
server (sinxronla) [boshlangichRoyxat] props, ochilgandan keyin o'zgarmaydi — bir marta
internet (online/offline) [vazifalar] "online" da joriy ro'yxat kerak
URL (popstate) [] faqat set funksiyalari va URL'dan o'qish
pagehide [] modul darajasidagi kechiktiruvchi

Uchinchi qatorga qarang. Server effekti [] emas, [boshlangichRoyxat] — chunki effekt ichida shu props ishlatiladi. Amalda u o'zgarmaydi (main.tsx uni bir marta beradi), shuning uchun effekt bir marta ishlaydi. Lekin massiv halol: agar kimdir keyinroq boshqa ro'yxat bersa, effekt qayta sinxronlaydi. Kanondagi izoh:

tsx
  // #28: server — ochilganda bir marta (boshlangichRoyxat o'zgarmaydi).

Ikkinchi va beshinchi qator — [], lekin ular aldash emas. Ichida ishlatilgan narsalar: setVazifalar, setTahrirId, setFiltr (barqaror set funksiyalari) va modul darajasidagi funksiyalar (filtrniOl, paketniTekshir). Reaktiv qiymat yo'q — ESLint ham jim. Kanondagi server-sinxron.ts tur izohida shu fakt yozilgan:

ts
/** App'ning state almashtiruvchilari (useState setter'lari — barqaror) */

To'rtinchi qator — eng qiziqarlisi. Kanon izohi (aynan):

tsx
  // #28: internet. "online" da joriy ro'yxat bilan solishtiramiz —
  // vazifalar bog'liqlik: har o'zgarishda tinglovchi yangisiga almashadi
  // (17/#31 da useEffectEvent bilan bu kerak bo'lmaydi)

Massiv halol — online ishlovchisi vazifalar ni o'qiydi. Lekin narx bor: har vazifa belgilanganda online tinglovchisi olib tashlanib, qayta ulanadi. Bu xato emas, lekin isrof. Uni useEffectEvent darsida o'lchab, tuzatamiz.

vazifalar ning ESLint sozlamasida exhaustive-deps — warn, npm run lint esa --max-warnings=0 bilan. Demak bitta sariq ogohlantirish ham commit'ni to'xtatadi (husky) va CI'ni yiqitadi. Kanonda bitta ham eslint-disable izohi yo'q.

7. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
effekt hech narsa o'zgarmasa ham ishlaydi massivda komponent ichidagi obyekt/funksiya primitiv maydon, effekt ichiga yoki tashqariga
The 'x' object makes the dependencies … change on every render o'sha o'sha
missing dependency: 'x' effekt x ni ishlatadi, massivda yo'q x ni qo'shing yoki effektni x siz yozing
ekran yangi, tashqi tizim eski massiv aldangan ([]) ESLint'ni o'chirmang, massivni to'g'rilang
[] + setCount(count + 1) — son 1 da qotadi eskirgan count updater: setCount((c) => c + 1)
massivni qo'shganda — cheksiz sikl effekt o'zi o'zgartirgan qiymatga bog'liq hosilaviy qiymat — effektsiz

Oxirgi qator haqida. ESLint "qo'shing" deydi, siz qo'shasiz — va sikl boshlanadi. Bu ESLint xatosi emas: u effekt ishlatgan narsani ko'rsatyapti. Muammo — effektning o'zida: u o'zi o'zgartirgan narsaga bog'liq. Bunday holatda massivni emas, effektni qayta o'ylang (useEffect I darsidagi 3-mashq).

8. Mashqlar

Mashqlar kurs/mashqlar/17/27-dependency-massivi/ da. Har yechimni «Ishga tushir» bilan sinang.

1-mashq (oson): effekt qayta ishlaydimi?

Har massiv uchun javob bering: oldingi chizishdan keyin price 35000, dish "Osh" edi. Yangi chizishda ham xuddi shunday (price — son, dish — satr, order — komponent ichida { dish, price } deb yaratilgan obyekt).

  • [price, dish] — effekt ishlaydimi (ha/yo'q):
  • [order] — effekt ishlaydimi (ha/yo'q):
  • [order.price] — effekt ishlaydimi (ha/yo'q):
Yechim

[price, dish] — Object.is(35000, 35000) va Object.is("Osh", "Osh") — ikkalasi true, effekt o'tkazib yuboriladi. [order] — order har chizishda yangi obyekt, Object.is — false, effekt ishlaydi. [order.price] — massivga son tushadi, qiymati o'sha — o'tkazib yuboriladi.

2-mashq (o'rta): tab nomini tuzating

Komponent tab nomini mehmon ismi va taomlar soni bilan yangilashi kerak, lekin massiv aldangan. Blok ismni yozib, taomni ikki marta qo'shadi. Massivni tuzating: konsolda har o'zgarishdan keyin to'g'ri tab nomi chiqsin, ortiqcha ishga tushish bo'lmasin. Effekt ichidagi label obyektiga e'tibor bering — «Uchta davo» bo'limidagi ikkinchi davo.

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

function App() {
  const [guest, setGuest] = useState("");
  const [count, setCount] = useState(0);
  const label = { guest, count };
  useEffect(() => {
    document.title = `${label.guest || "Mehmon"}: ${label.count} ta`;
    console.log("tab:", document.title);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
  return (
    <div>
      <input
        aria-label="Ism"
        value={guest}
        onChange={(e) => setGuest(e.target.value)}
      />
      <button onClick={() => setCount(count + 1)}>Taom +</button>
    </div>
  );
}

Konsolda:

text
tab: Mehmon: 0 ta
Yechim
jsx
import { useEffect, useState } from "react";

function App() {
  const [guest, setGuest] = useState("");
  const [count, setCount] = useState(0);
  useEffect(() => {
    const label = { guest, count }; // obyekt — effekt ichida
    document.title = `${label.guest || "Mehmon"}: ${label.count} ta`;
    console.log("tab:", document.title);
  }, [guest, count]);
  return (
    <div>
      <input
        aria-label="Ism"
        value={guest}
        onChange={(e) => setGuest(e.target.value)}
      />
      <button onClick={() => setCount(count + 1)}>Taom +</button>
    </div>
  );
}

Konsolda:

text
tab: Mehmon: 0 ta
tab: Malika: 0 ta
tab: Malika: 1 ta
tab: Malika: 2 ta

label effekt ichiga ko'chdi — massivda uning xom ashyosi: guest va count (ikkalasi primitiv). eslint-disable izohi olib tashlandi: ESLint endi jim, chunki massiv halol. [label] deb yozsangiz ham ishlardi, lekin effekt har chizishda ishga tushardi.

3-mashq (qiyin): obyekt prop'dan keladi

MenuBadge komponenti ota'dan options obyektini oladi: { category, highlight }. Ota har chizishda yangi obyekt beradi (<MenuBadge options={{ category, highlight: true }} />). Effekt category ga qarab tab nomini yozadi. Hozir ota'dagi boshqa state (n) o'zgarganda ham effekt ishlayapti. MenuBadge ni shunday o'zgartiringki, effekt faqat category o'zgarganda ishlasin. Ota'ga tegmang. Ishora: «Uchta davo» dagi birinchi davo — obyektdan primitiv maydonni effektdan oldin ajratib oling.

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

function MenuBadge({ options }) {
  useEffect(() => {
    document.title = `Bahor — ${options.category}`;
    console.log("effekt:", document.title);
  }, [options]);
  return <p>{options.category}</p>;
}

function App() {
  const [category, setCategory] = useState("taom");
  const [n, setN] = useState(0);
  return (
    <div>
      <MenuBadge options={{ category, highlight: true }} />
      <button onClick={() => setN(n + 1)}>+1</button>{" "}
      <button onClick={() => setCategory("ichimlik")}>
        Ichimlik
      </button>
    </div>
  );
}

Konsolda:

text
effekt: Bahor — taom
effekt: Bahor — taom
effekt: Bahor — ichimlik
effekt: Bahor — ichimlik
Yechim
jsx
import { useEffect, useState } from "react";

function MenuBadge({ options }) {
  const { category } = options; // primitiv — effektdan oldin
  useEffect(() => {
    document.title = `Bahor — ${category}`;
    console.log("effekt:", document.title);
  }, [category]);
  return <p>{category}</p>;
}

function App() {
  const [category, setCategory] = useState("taom");
  const [n, setN] = useState(0);
  return (
    <div>
      <MenuBadge options={{ category, highlight: true }} />
      <button onClick={() => setN(n + 1)}>+1</button>{" "}
      <button onClick={() => setCategory("ichimlik")}>
        Ichimlik
      </button>
    </div>
  );
}

Konsolda:

text
effekt: Bahor — taom
effekt: Bahor — ichimlik

To'rtta o'rniga ikkita. Komponent ota'ning obyektini boshqara olmaydi — u har chizishda yangi kelaveradi. Lekin komponent o'zi nimaga bog'liqligini tanlay oladi: options ga emas, category satriga. Destrukturizatsiya (Obyekt destructuring) bu yerda shunchaki qulaylik emas, balki bog'liqlikni toraytirish.

4-mashq: Vazifalar qadami

refactor/effektlar branch'ida «Vazifalar qadami: kanondagi massivlar» jadvalidagi oltita massivni o'z kodingiz bilan solishtiring. npm run lint ni ishga tushiring — exhaustive-deps dan bitta ham ogohlantirish bo'lmasin.

Yechim

Tekshiruv ro'yxati:

  • grep -n "eslint-disable" src/App.tsx — bo'sh.
  • Server effekti — [boshlangichRoyxat]. [] yozsangiz, ESLint missing dependency: 'boshlangichRoyxat' deydi.
  • Internet effekti — [vazifalar]. Bu ataylab: useEffectEvent darsigacha shunday qoladi.
  • Set funksiyalari (setVazifalar, setTarmoq, setTahrirId) hech bir massivda yo'q — ular barqaror.

9. Real ishda

  • exhaustive-deps — eng ko'p bahs qilinadigan ESLint qoidasi. Ko'p jamoalarda u error darajasiga ko'tariladi yoki --max-warnings=0 bilan ishlatiladi (vazifalar dagidek).
  • Obyekt bog'liqligi — chat ulanishlari, xarita vidjetlari va grafik kutubxonalarida ko'p uchraydigan sekinlik sababi: har chizishda qayta ulanish yoki qayta chizish. Profiler'da (Performance'ni o'lchash) birinchi qidiriladigan narsa.
  • React Compiler (React Compiler) komponent ichidagi obyekt va funksiyalarni avtomatik saqlab turadi. U yoqilgan loyihalarda bu darsdagi muammolarning bir qismi o'z-o'zidan yo'qoladi — lekin massivni aldash muammosi qoladi.
  • Intervyu savollari: "React bog'liqliklarni qanday solishtiradi?" (Object.is, har element alohida), "nega obyekt bog'liqligi effektni har safar ishga tushiradi?", "exhaustive-deps ni o'chirish nega xavfli?".

Xulosa

  • Massiv: yo'q — har chizishda; [] — bir marta; [a, b] — biri o'zgarganda. Birinchi chizishda effekt doim ishlaydi.
  • React har elementni Object.is bilan solishtiradi: primitivlar — qiymati, obyekt/massiv/funksiya — havolasi bo'yicha.
  • Komponent ichida yaratilgan obyekt va funksiya har chizishda yangi → effekt har safar ishlaydi. Davo: primitiv maydon, effekt ichiga yoki komponentdan tashqariga.
  • Massiv — effekt ishlatgan reaktiv qiymatlar ro'yxati (fakt, tanlov emas). Set funksiyalari va ref'lar barqaror.
  • Massivni aldash — eskirgan closure: ekran yangi, tashqi tizim eski. eslint-disable o'rniga kodni o'zgartiring.
  • vazifalar: oltita effektning massivi halol; --max-warnings=0 bitta ogohlantirishni ham o'tkazmaydi.

Keyingi dars: useEffect III: cleanup, race condition va AbortController — effekt boshlagan ishni (taymer, obuna, so'rov) qanday tozalash va kech kelgan javob ekranni buzmasligi.

Manbalar

  • React: useEffect — "Specifying reactive dependencies", "Removing unnecessary object/function dependencies" — react.dev/reference/react/useEffect
  • React: "Lifecycle of Reactive Effects", "Removing Effect Dependencies" — react.dev/learn
  • MDN: Object.is() — developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/is
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
useEffect II: dependency massivi chuqur — Object.is, obyekt va funksiya bog'liqliklari — IlmHamroh