IlmHamroh
JavaScript Full-stack/18-qism. React chuqur25/42-dars23 daqiqa
Mundarija (28)

React xatolari katalogi: eng ko'p uchraydigan xabarlar, sabablari va davosi

Qisqacha: React xatolarining aksariyati sakkiz-o'nta oilaga bo'linadi. Ular uch joyda tug'iladi: render paytida (renderda setState, obyektni JSX'ga berish, hook tartibi), effektda (bog'liqliksiz setState — cheksiz sikl, eskirgan closure) va state bilan ishlashda (mutatsiya, key, undefined dan qiymatga sakrash). Har xabarni shu uch savol bilan o'qing: xato yoki ogohlantirishmi, qaysi bosqichda, qaysi komponentda. Bu dars — katalog: xabarning asl matni, tarjimasi, ildiz sababi va davosi.

Bu darsda

  • React xabarini "xato yoki ogohlantirish, qaysi bosqich, qaysi komponent" tartibida o'qiysiz.
  • Too many re-renders, Cannot update a component while rendering, Objects are not valid as a React child ning ildiz sababini topasiz.
  • Cheksiz effekt siklini va eskirgan closure'ni (stale closure) ko'rib taniysiz va tuzatasiz.
  • Mutatsiya, key va controlled/uncontrolled almashishi qanday "jim" xatolar berishini tushuntira olasiz.
  • Xatoni yashiradigan "davolar"dan (ESLint'ni o'chirish, StrictMode ni olib tashlash) qochasiz.

Oldin bilishingiz kerak: React ilovani debug qilish, Sizga effekt kerak emas, State II: snapshot, batching va updater funksiya.

1. Nega bu kerak?

Oldingi darsda og'ir ishni UI'dan ajratdik. Endi kod ko'payib, «Bahor» ilovasiga yangi odam keldi. Sardor (stajyor) birinchi haftada uch marta qotib qoldi. Birinchi kuni sahifa oq bo'lib qoldi va konsolda Too many re-renders turardi. Ikkinchi kuni taymer 1 da to'xtab qoldi — hech qanday xabarsiz. Uchinchi kuni ro'yxatdan taom o'chirildi, lekin porsiya soni qo'shni taomga "ko'chib" o'tdi.

Uchala holatda ham Sardor xatoni uzoq qidirdi. Vaholanki bu xatolar React dasturchilarining hammasi qiladigan xatolar — ularning ro'yxati qisqa. Jasur aka ularni ko'zi bilan tanib oladi: "Bu — renderda setState", "bu — eskirgan closure". U aqlli bo'lgani uchun emas, shunchaki bu xatolarni ko'p ko'rgani uchun shunday.

Hayotiy o'xshatish: tajribali usta mashina ovozidan "bu — rezina", "bu — podshipnik" deydi. Uning boshida ovozlar katalogi bor. Bu dars sizga shunday katalog beradi. Xatolarning o'zi React ilovani debug qilish darsidagi usullar bilan topiladi. Bu yerda esa ularning "yuzi" — xabar matni va belgisi.

2. Xabarni o'qish tartibi

2.1 Uch savol

Har xabarni ko'rganda uchta savol bering:

  1. Xatomi yoki ogohlantirishmi? Uncaught Error: … — xato: komponent chizilmaydi, saytdagi natija oynasida «Komponentni chizishda xato» qutisi chiqadi. Oddiy matn (console.error) — ogohlantirish: ekran ishlayveradi, lekin nimadir noto'g'ri.
  2. Qaysi bosqichda? Render (komponent funksiyasi chaqirilganda), commit va effekt (ekranga yozilgandan keyin) yoki hodisa (tugma bosilganda). Xabarning o'zi ko'pincha aytadi: while rendering, inside useEffect.
  3. Qaysi komponentda? Xabardagi `App`, `Child` nomlari, Check the render method of … qatori va component stack.

Bu savollar sizni katalogning to'g'ri qatoriga olib boradi:

flowchart TD
  X[Konsolda xabar] --> T{Uncaught Error?}
  T -->|ha| R{Render paytida?}
  T -->|yo'q| O[Ogohlantirish]
  R -->|ha| R1[Too many re-renders<br/>Objects are not valid<br/>hook tartibi]
  R -->|effektda| E1[Maximum update depth]
  O --> O1[key, controlled,<br/>Cannot update]
  X2[Xabar yo'q, ekran noto'g'ri] --> J[Jim xatolar:<br/>stale closure, mutatsiya,<br/>indeks key]

Diagrammaning pastki qatoriga e'tibor bering. Eng qiyin xatolar umuman xabar bermaydi — ekran shunchaki noto'g'ri. Ularni faqat belgisidan tanish mumkin. Shuning uchun katalogda har xatoning belgisi ham yozilgan.

2.2 Ogohlantirish — ham xato

React ogohlantirishi development build'da chiqadi, production'da esa jim bo'ladi. Ko'p odam uni e'tiborsiz qoldiradi: "ishlayapti-ku". Lekin har ogohlantirish — ertangi xato. key ogohlantirishi bugun hech narsa buzmaydi, ertaga esa state qo'shni qatorga ko'chadi. Qoida: development'da konsol toza bo'lsin. vazifalar kanonining e2e tekshiruvi ham shunday ishlaydi: konsoldagi har console.error — tekshiruv muvaffaqiyatsiz.

Tekshirib ko'ring: Sahifa ishlayapti, lekin konsolda qizil Each child in a list should have a unique "key" prop. turibdi. Bu xatomi yoki ogohlantirishmi? Production'da foydalanuvchi uni ko'radimi?

Javob

Ogohlantirish: Uncaught yo'q, ekran chizilgan. Production build'da React bu tekshiruvni umuman qilmaydi — foydalanuvchi xabarni ko'rmaydi. Lekin muammoning o'zi (kalitsiz ro'yxat) production'da ham qoladi: tartib o'zgarganda state noto'g'ri elementga yopishishi mumkin. Shuning uchun ogohlantirish ham tuzatiladi.

3. Render paytidagi xatolar

Render — React komponent funksiyangizni chaqirib, JSX olayotgan payt (Pure komponentlar va render qoidasi). Bu payt sof bo'lishi kerak: hisoblang va qaytaring, boshqa hech narsa qilmang. Bu bo'limdagi xatolarning hammasi shu qoidaning buzilishi.

3.1 Too many re-renders

jsx
import { useState } from "react";

function App() {
  const [count, setCount] = useState(0);
  setCount(count + 1); // ❌ render ichida
  return <p>Buyurtmalar: {count}</p>;
}

Konsolda:

text
Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.

"Juda ko'p qayta render. React cheksiz siklning oldini olish uchun renderlar sonini cheklaydi." Zanjir: render → setCount → yangi render rejalashtirildi → yana render → yana setCount… React buni sanaydi va 25 ta ketma-ket renderdan keyin to'xtatadi.

Ikkinchi, ko'proq uchraydigan sababi bitta qavsda:

jsx
import { useState } from "react";

function App() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={setCount(count + 1)}>
      Buyurtma: {count}
    </button>
  );
}

Konsolda:

text
Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.

onClick={setCount(count + 1)} — funksiyani berish emas, renderda chaqirish. onClick ga setCount qaytargan qiymat (undefined) tushadi, setCount esa har renderda ishlaydi (Hodisalarni boshqarish). Davosi — o'q funksiya:

jsx
import { useState } from "react";

function App() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Buyurtma: {count}
    </button>
  );
}

Ekranda:

text
Buyurtma: 2

Birinchi variantning davosi esa boshqa: renderda setState kerak bo'lgan joyda odatda hosilaviy qiymat kerak edi. count dan hisoblanadigan narsa state bo'lmasin — renderda oddiy const bo'lsin (Sizga effekt kerak emas).

3.2 Cannot update a component while rendering a different component

Bola komponent render paytida otaning setter'ini chaqirsa:

jsx
import { useState } from "react";

function Total({ items, onTotal }) {
  const sum = items.reduce((s, d) => s + d.price, 0);
  onTotal(sum); // ❌ render paytida otani yangilaydi
  return <p>Taomlar: {items.length} ta</p>;
}

function App() {
  const [total, setTotal] = useState(0);
  const items = [{ price: 35000 }, { price: 5000 }];
  return (
    <div>
      <Total items={items} onTotal={setTotal} />
      <p>Jami: {total} so'm</p>
    </div>
  );
}

Konsolda:

text
Cannot update a component (`App`) while rendering a different component (`Total`). To locate the bad setState() call inside `Total`, follow the stack trace as described in https://react.dev/link/setstate-in-render

Ekranda:

text
Taomlar: 2 ta
Jami: 40000 so'm

"Total komponenti chizilayotganda boshqa komponentni (App) yangilab bo'lmaydi. Total ichidagi noto'g'ri setState() chaqiruvini topish uchun stack trace'ga qarang." Bu — ogohlantirish: ekran to'g'ri ko'rinadi. Lekin App bir renderda ikki marta chizildi va React render tartibiga ishonch yo'qoldi. Xabar ikkala ismni aniq aytadi: kim yangilandi (App) va kim yangiladi (Total).

Davosi — ma'lumotni yuqoriga "jo'natmaslik", balki yuqorida hisoblash. Jami summani items turgan joyda — App da — hisoblang:

jsx
function Total({ items }) {
  return <p>Taomlar: {items.length} ta</p>;
}

function App() {
  const items = [{ price: 35000 }, { price: 5000 }];
  const total = items.reduce((s, d) => s + d.price, 0); // ✅
  return (
    <div>
      <Total items={items} />
      <p>Jami: {total} so'm</p>
    </div>
  );
}

Ekranda:

text
Taomlar: 2 ta
Jami: 40000 so'm

total state emas — hosilaviy qiymat. Bitta render, ogohlantirish yo'q. Agar ota haqiqatan hodisa haqida bilishi kerak bo'lsa ("buyurtma tasdiqlandi"), uni hodisa ishlovchisida chaqiring, renderda emas (State'ni ko'tarish).

3.3 Objects are not valid as a React child

jsx
function App() {
  const dish = { name: "Osh", price: 35000 };
  return <p>{dish}</p>;
}

Konsolda:

text
Uncaught Error: Objects are not valid as a React child (found: object with keys {name, price}). If you meant to render a collection of children, use an array instead.

"Obyekt React bolasi bo'la olmaydi (topildi: name, price kalitli obyekt)." Bu xatoni JSX II darsida ko'rgansiz. Real ishda u ko'pincha serverdan kelgan ma'lumotda chiqadi: {order.customer} deb yozasiz, customer esa satr emas, { id, name } obyekti. Xabardagi kalitlar ro'yxati — eng yaxshi ishora: {id, name} ni ko'rsangiz, .name ni unutgansiz. Date obyekti ham shu xatoni beradi.

Yaqin qarindoshi — funksiya:

jsx
function App() {
  const greet = () => "Xush kelibsiz!";
  return <p>{greet}</p>;
}

Konsolda:

text
Functions are not valid as a React child. This may happen if you return greet instead of <greet /> from render. Or maybe you meant to call this function rather than return it.
  <p>{greet}</p>

Bu — ogohlantirish, ekran esa bo'sh <p>. Xabar ikki taxmin beradi: komponent sifatida <Greet /> yozmoqchi edingiz yoki funksiyani chaqirmoqchi edingiz — {greet()}.

3.4 Hook tartibi buzilsa

jsx
import { useState } from "react";

function App() {
  const [vip, setVip] = useState(false);
  if (vip) {
    const [percent] = useState(10); // ❌ shart ichida hook
  }
  return <button onClick={() => setVip(true)}>Chegirma</button>;
}

Konsolda:

text
React has detected a change in the order of Hooks called by App. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://react.dev/link/rules-of-hooks
   Previous render            Next render
   ------------------------------------------------------
1. useState                   useState
2. undefined                  useState
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Uncaught Error: Rendered more hooks than during the previous render.

"Oldingi renderdagidan ko'proq hook chaqirildi." React hooklarni nomi bilan emas, tartib raqami bilan eslab qoladi: birinchi useState, ikkinchi useState… (Hooklar qoidalari). Birinchi renderda bitta hook bor edi, bosilgandan keyin — ikkita. Ro'yxat mos kelmay qoldi. Davosi — hookni doim yuqori darajada chaqirish, shartni esa qiymatga qo'yish: const [percent] = useState(10); va keyin vip ? percent : 0. ESLint'ning react-hooks/rules-of-hooks qoidasi buni kod yozayotganda ushlaydi — u vazifalar da 17-qismdan beri yoqilgan.

Tekshirib ko'ring: <button onClick={handleClick()}> — handleClick faqat console.log("bosildi") qiladi, setState yo'q. Too many re-renders chiqadimi? Nima bo'ladi?

Javob

Chiqmaydi: render ichida state o'zgarmaydi, sikl yo'q. Lekin xato baribir bor — "bosildi" har renderda yoziladi, bosganda esa hech narsa bo'lmaydi (onClick ga undefined tushgan). Bu "jim" variant: xabarsiz, faqat belgi bilan tanisa bo'ladi. Davosi o'sha — onClick={handleClick} yoki onClick={() => handleClick()}.

4. Effekt xatolari

4.1 Cheksiz effekt sikli

Bog'liqliklar massivisiz effekt har commit'dan keyin ishlaydi (useEffect II). U ichida state o'zgartirsa — yangi render, yangi commit, yana effekt. Quyidagi misolda biz sikl 60 da to'xtashi uchun shart qo'ydik (aks holda u abadiy aylanadi):

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

function App() {
  const [n, setN] = useState(0);
  useEffect(() => {
    if (n < 60) setN(n + 1); // ❌ bog'liqliklarsiz effektda setState
  });
  return <p>Render: {n}</p>;
}

Konsolda:

text
Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.

Ekranda:

text
Render: 60

"Yangilanishlar chuqurligi oshib ketdi. Komponent useEffect ichida setState chaqirsa va useEffect da bog'liqliklar massivi bo'lmasa yoki bog'liqliklardan biri har renderda o'zgarsa, shunday bo'ladi." React effekt ichidan kelgan ketma-ket yangilanishlarni sanaydi va 50 dan oshganda shu ogohlantirishni beradi. Xatodan farqli o'laroq, u siklni to'xtatmaydi. Shartimiz bo'lmasa, sahifa ishlayveradi, protsessor esa 100% band bo'ladi va telefon qiziydi.

Xabar ikki sababni aytadi. Birinchisi — massiv yo'q (yuqoridagi). Ikkinchisi yashirinroq: massiv bor, lekin bog'liqlik har renderda yangi:

jsx
function Menu() {
  const [dishes, setDishes] = useState([]);
  const filter = { category: "ovqat" }; // ❌ har renderda yangi obyekt
  useEffect(() => {
    setDishes(loadDishes(filter));
  }, [filter]); // Object.is: doim "o'zgardi"
}

React bog'liqliklarni Object.is bilan solishtiradi. Ikki xil obyekt — mazmuni bir xil bo'lsa ham — "har xil". Davolari, eng yaxshisidan boshlab:

  1. Effekt umuman kerakmi? Ma'lumot props'dan hisoblanadigan bo'lsa — renderda hisoblang (Sizga effekt kerak emas).
  2. Bog'liqlikka obyekt emas, primitiv bering: [filter.category].
  3. Obyektni komponentdan tashqarida e'lon qiling yoki effekt ichiga ko'chiring.
  4. Oxirgi chora — useMemo (memo, useMemo, useCallback).

4.2 Eskirgan closure (stale closure)

Bu — jim xato. Xabar yo'q, ekran shunchaki to'xtaydi:

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

function App() {
  const [sec, setSec] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setSec(sec + 1), 100);
    return () => clearInterval(id);
  }, []); // ❌ sec — birinchi renderdagi 0
  return <p>Osh tayyor bo'lishiga: {sec} s</p>;
}

Ekranda:

text
Osh tayyor bo'lishiga: 1 s

Taymer 450 ms ichida to'rt marta ishladi, ekranda esa doim 1. Sabab — snapshot: interval funksiyasi birinchi renderda yasalgan va o'sha renderdagi sec ni — 0 ni — "ushlab qolgan". Har safar u setSec(0 + 1) deydi. Bo'sh massiv [] effektni qayta yasamaydi, demak yangi sec ni ko'radigan funksiya hech qachon paydo bo'lmaydi.

Davosi — updater funksiya: "joriy qiymatni sen ber, men unga 1 qo'shaman":

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

function App() {
  const [sec, setSec] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setSec((s) => s + 1), 100);
    return () => clearInterval(id);
  }, []); // ✅ eski qiymatga tayanmaydi
  return <p>Osh tayyor bo'lishiga: {sec} s</p>;
}

Ekranda:

text
Osh tayyor bo'lishiga: 4 s

Eskirgan closure faqat taymerda emas. Uning uyalari: setInterval, addEventListener dagi ishlovchi, useCallback(…, []) va bog'liqliklari to'liq bo'lmagan har qanday effekt. ESLint'ning react-hooks/exhaustive-deps qoidasi ko'p holatni ushlaydi — shuning uchun uni o'chirib qo'ymang (pastda «Davoning o'zi xato bo'lsa» bo'limida). Effektda eng so'nggi qiymatni o'qish, lekin effektni qayta ishga tushirmaslik kerak bo'lsa — useEffectEvent.

Tekshirib ko'ring: Eskirgan closure misolida massivga [sec] qo'yilsa, taymer ishlaydimi? Updater variantidan nimasi yomonroq?

Javob

Ishlaydi: har sec o'zgarganda effekt tozalanadi va yangi sec bilan yangi interval yasaladi. Lekin har soniyada interval o'chirilib qayta yoqiladi — ortiqcha ish. Bundan tashqari interval har safar noldan sanaydi, shuning uchun ritm buziladi. Updater variantida bitta interval butun umr ishlaydi va hech qanday qiymatni "ushlab" qolmaydi.

5. State bilan ishlashdagi xatolar

5.1 Mutatsiya — ekran yangilanmaydi

jsx
import { useState } from "react";

function App() {
  const [order, setOrder] = useState(["Osh"]);
  function addManti() {
    order.push("Manti"); // ❌ eski massivni o'zgartirdi
    setOrder(order); // o'sha massiv — React "o'zgarmadi" deydi
    console.log(order);
  }
  return (
    <div>
      <p>Buyurtma: {order.join(", ")}</p>
      <button onClick={addManti}>Manti qo'shish</button>
    </div>
  );
}

Konsolda:

text
(2) ['Osh', 'Manti']

Ekranda:

text
Buyurtma: Osh
Manti qo'shish

Konsolda massivda ikki taom, ekranda — bitta. Bu ziddiyat mutatsiyaning aniq belgisi. React setOrder(order) ni Object.is(eski, yangi) bilan tekshiradi: havola o'sha — "o'zgarish yo'q", render bo'lmaydi. Davosi — har doim yangi massiv yoki obyekt (State III):

jsx
import { useState } from "react";

function App() {
  const [order, setOrder] = useState(["Osh"]);
  function addManti() {
    setOrder((o) => [...o, "Manti"]); // ✅ yangi massiv
  }
  return (
    <div>
      <p>Buyurtma: {order.join(", ")}</p>
      <button onClick={addManti}>Manti qo'shish</button>
    </div>
  );
}

Ekranda:

text
Buyurtma: Osh, Manti
Manti qo'shish

Mutatsiyaning yana bir yuzi bor: ekran keyinroq, boshqa sabab bilan yangilanganda o'zgarish birdan chiqadi. "Tugma ishlamadi, keyin boshqasini bossam — paydo bo'ldi" — shu belgi. memo va useMemo esa mutatsiya bilan butunlay adashadi (memo, useMemo, useCallback). Mutatsiyalar ko'p joyda yashirinadi: sort(), reverse(), splice() — ular asl massivni o'zgartiradi. Ularning nusxa qaytaradigan juftlari bor: toSorted(), toReversed(), toSpliced().

5.2 Controlled va uncontrolled almashishi

jsx
import { useState } from "react";

function App() {
  const [form, setForm] = useState({}); // ❌ form.name — undefined
  return (
    <input
      placeholder="Ism"
      value={form.name}
      onChange={(e) => setForm({ name: e.target.value })}
    />
  );
}

Konsolda:

text
A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://react.dev/link/controlled-components

"Komponent uncontrolled input'ni controlled'ga aylantiryapti. Ehtimol value undefined dan aniq qiymatga o'zgargan — bunday bo'lmasligi kerak. Komponent umri davomida bittasini tanlang." Birinchi renderda value={undefined} — React uchun bu "value umuman yo'q", ya'ni uncontrolled maydon (Formalar I). Birinchi harfdan keyin value="M" — endi controlled. Rejim o'rtada almashdi.

Davosi — boshlang'ich qiymat: useState({ name: "" }) yoki value={form.name ?? ""}. Teskari yo'nalish ham bor: value qiymatdan undefined ga o'tsa, A component is changing a controlled input to be uncontrolled chiqadi. Odatda sababi serverdan kelgan ma'lumotda null maydonda bo'ladi. Bu qoida sizning komponentlaringizga ham tegishli — Controlled va uncontrolled komponent API dizayni darsida o'zingiz shunday rejimli komponent yasaysiz.

6. Ro'yxat va key

6.1 Kalitsiz va takror kalit

jsx
const MENU = ["Osh", "Manti"];

function App() {
  return <ul>{MENU.map((d) => <li>{d}</li>)}</ul>;
}

Konsolda:

text
Each child in a list should have a unique "key" prop.
Check the render method of `App`. See https://react.dev/link/warning-keys for more information.

"Ro'yxatdagi har bola noyob key props'iga ega bo'lishi kerak. App ning render metodini tekshiring." Ikkinchi qator komponentni aytadi. Takror kalit — boshqa xabar:

jsx
const MENU = ["Osh", "Osh", "Manti"];

function App() {
  return <ul>{MENU.map((d) => <li key={d}>{d}</li>)}</ul>;
}

Konsolda:

text
Encountered two children with the same key, `Osh`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.

"Bir xil kalitli ikki bola topildi: Osh. Kalitlar noyob bo'lsin — shunda komponentlar yangilanishlar orasida o'z shaxsini saqlaydi. Takror kalit bolalarni ikki marta chizishi yoki tushirib qoldirishi mumkin." Ikki mehmon bir xil taom buyurtma qilsa — nom kalit bo'la olmaydi. Kalit — ma'lumotning id si.

6.2 Indeks kalit — jim xato

Eng xavflisi — ogohlantirishni "o'chirish" uchun key={index} qo'yish. Ogohlantirish yo'qoladi, xato qoladi:

jsx
import { useState } from "react";

function Row({ name, onRemove }) {
  const [portions, setPortions] = useState(1);
  return (
    <li>
      {name}: {portions} porsiya{" "}
      <button onClick={() => setPortions(portions + 1)}>
        + {name}
      </button>{" "}
      <button onClick={onRemove}>{name} — o'chir</button>
    </li>
  );
}

function App() {
  const [dishes, setDishes] = useState(["Osh", "Manti"]);
  return (
    <ul>
      {dishes.map((d, i) => (
        <Row
          key={i} // ❌ indeks — o'rin, taom emas
          name={d}
          onRemove={() => setDishes(dishes.filter((x) => x !== d))}
        />
      ))}
    </ul>
  );
}

Ekranda:

text
Manti: 1 porsiya + Manti Manti — o'chir

Mantiga uch porsiya qo'shdik (1 + 2), Oshni o'chirdik — Mantida esa 1 porsiya. Nima bo'ldi? O'chirishdan oldin: kalit 0 — Osh (1 porsiya), kalit 1 — Manti (3 porsiya). O'chirgandan keyin Manti birinchi o'ringa chiqdi va kalit 0 ni oldi. React kalit 0 dagi komponentni saqlab qoldi — u bilan birga Oshning state'ini (1). Kalit 1 esa yo'qoldi, Mantining 3 porsiyasi u bilan ketdi.

Kalit 0 Kalit 1
O'chirishdan oldin Osh, 1 porsiya Manti, 3 porsiya
Keyin (indeks) Manti, 1 porsiya — Oshniki yo'q — 3 porsiya yo'qoldi
Keyin (key={d}) — Manti, 3 porsiya

key={d} (yoki haqiqiy id) bilan sinab ko'ring — Mantida 3 porsiya qoladi. Indeks kalit faqat ro'yxat hech qachon qayta tartiblanmasa, o'chirilmasa va qatorlarda state bo'lmasa xavfsiz. Batafsil — Ro'yxatlarni render qilish va key.

Tekshirib ko'ring: Sardor key={Math.random()} qo'ydi — "noyob-ku!". Ogohlantirish yo'qoldi. Nima buzildi?

Javob

Har renderda hamma kalit yangi. React har qatorni "yangi element" deb ko'radi: eskisini o'chiradi (state, fokus, input matni yo'qoladi) va noldan yaratadi. Bitta harf yozilsa ham butun ro'yxat qayta yasaladi — sekin va maydondagi fokus uchib ketadi. Kalit noyob bo'lishi bilan birga barqaror bo'lishi kerak: bir element — har renderda bir kalit.

7. Katalog: bir jadvalda

Belgi yoki xabar Turi Ildiz sabab Davo
Too many re-renders xato renderda setState, onClick={f()} hosilaviy qiymat; onClick={() => f()}
Cannot update a component (A) while rendering (B) ogohl. bola renderda ota setter'ini chaqiradi otada hisoblash; hodisada chaqirish
Objects are not valid as a React child xato obyekt yoki Date JSX'da kerakli maydon: .name
Functions are not valid as a React child ogohl. {f} — chaqirilmagan {f()} yoki <F />
Rendered more/fewer hooks… xato shart, sikl, erta return dan keyin hook hooklar yuqorida, shart — qiymatda
Maximum update depth exceeded ogohl. yoki xato effektda setState, bog'liqlik har renderda yangi massiv; primitiv bog'liqlik; effektsiz
Qiymat "qotib qolgan" jim eskirgan closure updater; to'liq bog'liqliklar
Konsolda bor, ekranda yo'q jim mutatsiya yangi massiv yoki obyekt
uncontrolled … to be controlled ogohl. value={undefined} ?? "" yoki boshlang'ich qiymat
unique "key", same key ogohl. kalit yo'q yoki takror ma'lumot id si
State qo'shni qatorga ko'chdi jim key={index} barqaror id

Maximum update depth exceeded ikki xil bo'ladi. Effektda u ogohlantirish (yuqorida ko'rdik). useLayoutEffect dagi sinxron sikl esa Uncaught Error beradi — ikkinchi qismida componentWillUpdate or componentDidUpdate deb yozilgan. Bu class komponentlar davridan qolgan matn, lekin funksiya komponentlarda ham chiqadi (Class komponentlarni o'qish).

8. Ko'p uchraydigan xatolar: davoning o'zi xato bo'lsa

Katalogni bilish yarim ish. Ikkinchi yarmi — xabarni yo'qotish emas, sababni tuzatish. Sardorning uchta "tezkor davosi" va ular nimani yashiradi:

"Davo" Nimani yashiradi To'g'risi
// eslint-disable-next-line react-hooks/exhaustive-deps eskirgan closure yoki keraksiz effekt effektni qayta o'ylash: kerakmi, updater, useEffectEvent
<StrictMode> ni olib tashlash ("effekt ikki marta ishlayapti") cleanup'siz effekt — production'da ham sizib chiqadi cleanup yozish (StrictMode chuqur)
key={index} yoki key={Math.random()} identifikatsiya yo'qligi ma'lumotdagi barqaror id
try/catch render atrofida renderdagi xato try ga tushmaydi — bola komponentlar keyin chiziladi Error boundary
useRef bayrog'i: "effekt ikkinchi marta ishlamasin" StrictMode ko'rsatgan cleanup yo'qligi cleanup; qayta ishlashga chidamli effekt

Umumiy qoida: ogohlantirish — alomat. Haroratni tushiradigan dori kasallikni davolamaydi. ESLint qoidasini o'chirishdan oldin o'zingizga savol bering: "U nima demoqchi?" Ko'pincha javob — "bu effekt kerak emas".

9. Mashqlar

Mashqlar kurs/mashqlar/18/25-react-xatolari/ da. Har yechimni «Ishga tushir» bilan sinang.

1-mashq (oson): xabardan sababga

Har xabar yoki belgi uchun ildiz sababni yozing (render, closure, mutatsiya, key, undefined — shu so'zlardan biri):

  • Too many re-renders — renderda chaqirilgan setter:
  • Taymer ekranda 1 da to'xtab qoldi, xabar yo'q:
  • console.log da yangi taom bor, ekranda yo'q:
  • O'chirgandan keyin porsiya soni qo'shni taomga o'tdi:
  • changing an uncontrolled input to be controlled:
Yechim

Birinchisi — render paytidagi setState («Too many re-renders» bo'limi). Ikkinchisi — eskirgan closure: taymer funksiyasi birinchi renderdagi qiymatni ushlab qolgan. Uchinchisi — mutatsiya: massiv o'zgardi, havola esa o'sha, React render qilmadi. To'rtinchisi — indeks kalit: state o'ringa yopishgan, taomga emas. Beshinchisi — value boshida undefined edi.

2-mashq (o'rta): navbat taymeri

«Bahor» oshxonasida navbat raqami har 100 ms da bittaga oshishi kerak. Lekin ekranda raqam 1 da qotib qoladi. Tuzating — 450 ms dan keyin Navbat: 4 chiqsin. Ishora: «Eskirgan closure» bo'limi — updater funksiya.

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

function App() {
  const [ticket, setTicket] = useState(0);
  useEffect(() => {
    const id = setInterval(() => {
      setTicket(ticket + 1);
    }, 100);
    return () => clearInterval(id);
  }, []);
  return <p>Navbat: {ticket}</p>;
}

Ekranda:

text
Navbat: 1
Yechim
jsx
import { useEffect, useState } from "react";

function App() {
  const [ticket, setTicket] = useState(0);
  useEffect(() => {
    const id = setInterval(() => {
      setTicket((t) => t + 1); // ✅ joriy qiymatdan
    }, 100);
    return () => clearInterval(id);
  }, []);
  return <p>Navbat: {ticket}</p>;
}

Ekranda:

text
Navbat: 4

Interval funksiyasi birinchi renderda yasalgan va ticket = 0 ni ushlab qolgan edi. Updater funksiya ticket ni o'qimaydi — React unga joriy qiymatni o'zi beradi. [ticket] bog'liqligi ham ishlardi, lekin har qadamda intervalni qayta yasardi.

3-mashq (qiyin): uch xatoli savat

Sardor savat yozdi. Unda uchta xato bor: konsolda ikkita ogohlantirish chiqadi, «Choy qo'shish» tugmasi esa ekranni yangilamaydi. Uchalasini tuzating: konsol toza bo'lsin, ikki marta bosilgandan keyin ekranda uch taom va to'g'ri jami chiqsin.

Ishora: «Katalog: bir jadvalda» — har belgini qatoriga moslang. Mutatsiya uchun — State III, key uchun — taomning id si.

jsx
import { useState } from "react";

let nextId = 3;

function App() {
  const [cart, setCart] = useState([
    { id: 1, name: "Osh", price: 35000 },
    { id: 2, name: "Manti", price: 30000 },
  ]);
  const [note, setNote] = useState();

  function addTea() {
    cart.push({ id: nextId++, name: "Ko'k choy", price: 5000 });
    setCart(cart);
  }

  const total = cart.reduce((s, d) => s + d.price, 0);
  return (
    <div>
      <ul>
        {cart.map((d) => (
          <li>{d.name} — {d.price}</li>
        ))}
      </ul>
      <p>Jami: {total} so'm</p>
      <input
        placeholder="Izoh"
        value={note}
        onChange={(e) => setNote(e.target.value)}
      />
      <button onClick={addTea}>Choy qo'shish</button>
    </div>
  );
}

Konsolda:

text
Each child in a list should have a unique "key" prop.
Check the render method of `App`. See https://react.dev/link/warning-keys for more information.

Ekranda:

text
Osh — 35000
Manti — 30000
Jami: 65000 so'm
Choy qo'shish

Konsolda hozircha bitta ogohlantirish. Ikkinchisi (uncontrolled … to be controlled) izoh maydoniga birinchi harf yozilganda chiqadi — «Ishga tushir» bilan yozib ko'ring.

Yechim
jsx
import { useState } from "react";

let nextId = 3;

function App() {
  const [cart, setCart] = useState([
    { id: 1, name: "Osh", price: 35000 },
    { id: 2, name: "Manti", price: 30000 },
  ]);
  const [note, setNote] = useState(""); // ✅ boshidan satr

  function addTea() {
    const tea = { id: nextId++, name: "Ko'k choy", price: 5000 };
    setCart((c) => [...c, tea]); // ✅ yangi massiv
  }

  const total = cart.reduce((s, d) => s + d.price, 0);
  return (
    <div>
      <ul>
        {cart.map((d) => (
          <li key={d.id}>{d.name} — {d.price}</li> // ✅ id
        ))}
      </ul>
      <p>Jami: {total} so'm</p>
      <input
        placeholder="Izoh"
        value={note}
        onChange={(e) => setNote(e.target.value)}
      />
      <button onClick={addTea}>Choy qo'shish</button>
    </div>
  );
}

Ekranda:

text
Osh — 35000
Manti — 30000
Ko'k choy — 5000
Ko'k choy — 5000
Jami: 75000 so'm
Choy qo'shish

Uch tuzatish: key={d.id} (ikki choy bir xil nomli — nom kalit bo'la olmasdi), setCart((c) => [...c, tea]) (yangi massiv — React o'zgarishni ko'radi) va useState("") (izoh maydoni boshidan controlled). Diqqat: tea obyekti updater'dan tashqarida yasaladi. nextId++ ni updater ichiga qo'ysangiz, StrictMode dev'da updater'ni ikki marta chaqiradi va id'lar sakrab ketadi. Updater sof bo'lishi kerak (StrictMode chuqur).

10. Real ishda

  • Kod ko'rigi (code review): katalogdagi jim xatolar — ko'rikda eng ko'p topiladigan narsalar. Jasur aka PR'da birinchi navbatda key={index}, push/sort va eslint-disable qatorlarini qidiradi.
  • Sentry va xato kuzatuvi: production'da ogohlantirishlar yo'q, faqat Uncaught Error lar keladi. Ular qisqartirilgan bo'ladi: masalan Minified React error #301 va havola — bu Too many re-renders. Raqamni react.dev'dagi ro'yxatdan ochasiz — matni shu katalogdagilar.
  • ESLint: eslint-plugin-react-hooks (kanonda 7.1.1) katalogning uchdan birini kod yozayotganda ushlaydi: hooklar qoidasi, bog'liqliklar, renderda setState. React Compiler lint qoidalari ham shu plaginda (React Compiler).
  • Intervyu savollari: "Nega key={index} yomon?", "Stale closure nima va qanday tuzatiladi?", "Too many re-renders qachon chiqadi?", "Nega state'ni mutatsiya qilib bo'lmaydi?".

Xulosa

  • Har xabarga uch savol: xatomi yoki ogohlantirishmi, qaysi bosqichda, qaysi komponentda.
  • Render sof bo'lsin: renderda setState, ota setter'ini chaqirish, shartdagi hook — hammasi xato yoki ogohlantirish beradi.
  • Effektda setState — bog'liqliklar massivi va barqaror (primitiv) bog'liqliklar bilan; aks holda Maximum update depth exceeded.
  • Jim xatolar — eskirgan closure (updater bilan davolanadi), mutatsiya (yangi massiv), indeks kalit (barqaror id).
  • value hech qachon undefined bo'lmasin — maydon rejimi umr bo'yi bitta.
  • Ogohlantirishni yashirmang (eslint-disable, StrictMode siz, tasodifiy key) — sababini tuzating.

Keyingi dars: Compound komponentlar va slot naqshi — bir-biriga bog'langan komponentlar umumiy holatni Context orqali qanday bo'lishishi.

Manbalar

  • React: "Common pitfalls" — react.dev/reference/react/useState#troubleshooting
  • React: useEffect troubleshooting — react.dev/reference/react/useEffect#troubleshooting
  • React: "Rules of Hooks" — react.dev/reference/rules/rules-of-hooks
  • React: "Rendering Lists" (key) — react.dev/learn/rendering-lists
  • React xato kodlari (minified) — react.dev/errors
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
React xatolari katalogi: eng ko'p uchraydigan xabarlar, sabablari va davosi — IlmHamroh