Mundarija (26)
- Bu darsda
- 1. Nega bu kerak?
- 2. React hooklarni qanday eslaydi
- 2.1 Komponent — har safar qaytadan chaqiriladigan funksiya
- 2.2 O'zimiz yasab ko'ramiz
- 2.3 Haqiqiy React: uch xil natija
- 3. Ikki qoida
- 3.1 Birinchi qoida: faqat yuqori darajada
- 3.2 Ikkinchi qoida: faqat React funksiyalaridan
- 4. ESLint qoidani qanday ushlaydi
- 4.1 Haqiqiy xabarlar
- 4.2 TypeScript bu xatoni ko'rmaydi
- 5. Shartli hookni to'g'ri yozish
- 5.1 Hookni yuqoriga, shartni pastga
- 5.2 Shartni komponentga aylantirish
- 5.3 Ro'yxat uchun — massiv bitta state'da
- 6. use() — yagona istisno
- 7. Vazifalar o'lchovi: ESLint va TypeScript
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): qaysi biri buzadi?
- 2-mashq (o'rta): Sardorning tahrir qatorini tuzating
- 3-mashq (qiyin): modelga qo'riqchi qo'shing
- 10. Real ishda
- Xulosa
- Manbalar
Hooklar qoidalari: nega hook if ichida chaqirilmaydi va React ularni qanday eslaydi
Qisqacha: React har komponentning hooklarini nomi bo'yicha emas, chaqirilish tartibi bo'yicha eslaydi: birinchi
useState— 1-katak, ikkinchisi — 2-katak. Shuning uchun ikki qoida bor: hook faqat komponent (yokiusebilan boshlanadigan custom hook) ichida va faqat yuqori darajada chaqiriladi —if, sikl, ertareturndan keyin, ichki funksiya yoki hodisa ishlovchisida emas. Qoida buzilsa, ReactRendered more hooks than during the previous renderdeydi yoki, eng yomoni, qiymatlarni jim almashtirib yuboradi. ESLint'ningreact-hooks/rules-of-hooksqoidasi buni kod yozayotganda ushlaydi; TypeScript esa ko'rmaydi. Yagona istisno — React 19 dagiuse(): uni shart ichida chaqirsa bo'ladi.
Bu darsda
- React hooklarni qanday eslashini o'z qo'lingiz bilan yozilgan 20 qatorlik model orqali tushuntira olasiz.
- Hooklarning ikki qoidasini va ularning har bir buzilish shaklini taniysiz.
Rendered more hooks…,Rendered fewer hooks…va "tartib o'zgardi" xabarlarini o'qib, sababini topasiz.- ESLint
rules-of-hooksxabarlarini tushunasiz va shartli hookni to'g'ri shaklga keltirasiz. use()nega istisno ekanini bilasiz.
Oldin bilishingiz kerak: State I: useState — komponent xotirasi, Shartli render, ESLint II: React va TypeScript loyiha uchun plaginlar.
1. Nega bu kerak?
Mini-ilova darsida vazifalar ning butun interfeysi React'ga o'tdi. Endi oldinda yangi hooklar bor: useEffect, useRef, useEffectEvent. Ularning hammasi bitta umumiy qoidaga bo'ysunadi. Uni bugun bir marta yaxshilab tushunib olsak, keyingi o'nta darsda qoqilmaymiz.
Sardor tahrir qatori uchun kichik komponent yozdi. Mantiq oddiy edi: "tahrir rejimida bo'lmasa — hech narsa chizma; bo'lsa — maydon va uning state'i":
export function Tajriba({ tahrirda }: { tahrirda: boolean }) {
if (!tahrirda) {
return null;
}
const [matn, setMatn] = useState("");
return <input value={matn} onChange={(e) => setMatn(e.target.value)} />;
}Kod o'qilishi chiroyli. TypeScript ham jim: npx tsc -b — exit 0. Lekin vazifalar dagi ESLint darhol qizil chiziq chizdi (haqiqiy chiqish):
7:27 error React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return? react-hooks/rules-of-hooks"useState hooki shart ostida chaqirilgan. React hooklari har chizishda aynan bir xil tartibda chaqirilishi shart. Tasodifan erta return dan keyin chaqirmadingizmi?" Sardor hayron: "Funksiya-ku, if ichida nima chaqirsam ham bo'lardi-ku?"
Oddiy funksiyada — ha. Hook esa boshqacha. Hayotiy o'xshatish: kiyim saqlash xonasi (garderob). Siz paltongizni topshirasiz, sizga raqam beriladi — 1, 2, 3. Garderobchi ismingizni so'ramaydi, faqat raqamga qaraydi. Agar kimdir navbatsiz kirib, raqamlar siljib ketsa — siz birovning paltosini olib ketasiz. React hooklari aynan shunday raqamlangan.
2. React hooklarni qanday eslaydi
2.1 Komponent — har safar qaytadan chaqiriladigan funksiya
useState darsida ko'rganimizdek, komponent har chizishda boshidan oxirigacha qayta chaqiriladi. Uning ichidagi let va const lar har safar yangidan yaratiladi. Unda state qayerda yashaydi? Komponent funksiyasining ichida emas — React'ning o'zida, shu komponentga bog'langan ro'yxatda.
useState("Osh") chaqirilganda funksiya o'zidan "qaysi state?" deb so'ramaydi. Unga nom berilmaydi — dish degan nom faqat sizning kodingizda. React faqat bitta narsani biladi: "bu chizishdagi nechanchi hook chaqiruvi".
2.2 O'zimiz yasab ko'ramiz
Buni tushunishning eng yaxshi yo'li — React'ning soddalashtirilgan modelini o'zimiz yozish. Haqiqiy React ancha murakkab, lekin g'oya aynan shu: ro'yxat va hisoblagich.
// React xotirasining soddalashtirilgan modeli
const slots = []; // komponent xotirasi: katakchalar ro'yxati
let index = 0; // hozir nechanchi hook chaqirilyapti
function useState(initial) {
const i = index; // shu chaqiruvning raqami
index++;
if (slots.length <= i) slots.push(initial); // birinchi marta
const setState = (value) => {
slots[i] = value;
};
return [slots[i], setState];
}
function render(component, props) {
index = 0; // har chizish boshida hisob noldan
return component(props);
}
function OrderCard({ showNote }) {
const [dish] = useState("Osh");
let note = "";
if (showNote) {
[note] = useState("piyozsiz"); // ❌ shart ichida
}
const [count] = useState(1);
return `${dish} × ${count}${note ? ", " + note : ""}`;
}
console.log(render(OrderCard, { showNote: false })); // Osh × 1
console.log(render(OrderCard, { showNote: true })); // Osh × 1, 1
console.log(slots); // [ 'Osh', 1, 1 ]Ikkinchi chizishda izoh "piyozsiz" bo'lishi kerak edi, lekin ekranda 1 — sonning qiymati! Nima bo'ldi? Qadamma-qadam ko'ramiz:
| Chaqiruv | 1-chizish (showNote: false) |
2-chizish (showNote: true) |
|---|---|---|
| 1-hook | dish → katak 0 = "Osh" |
dish → katak 0 = "Osh" |
| 2-hook | count → katak 1 = 1 |
note → katak 1 = 1 |
| 3-hook | — | count → katak 2 yangi = 1 |
Birinchi chizishda katak 1 ni count egallagan. Ikkinchi chizishda shart ochildi va katak 1 ni note "o'qib oldi". count esa yangi, bo'sh katakka tushdi. Hech qanday xato chiqmadi — model shunchaki noto'g'ri qiymat berdi. Garderobda raqamlar siljidi.
flowchart LR
subgraph R1["1-chizish"]
A1["useState: dish"] --> K0["katak 0: Osh"]
B1["useState: count"] --> K1["katak 1: 1"]
end
subgraph R2["2-chizish, shart ochiq"]
A2["useState: dish"] --> K0b["katak 0: Osh"]
B2["useState: note"] --> K1b["katak 1: 1 ❌"]
C2["useState: count"] --> K2b["katak 2: yangi"]
endDiagrammada chap ustun — birinchi chizish, o'ng ustun — ikkinchi. O'ng tomonda note ga qarang: u boshqa hookning katagiga tushib qolgan.
Tekshirib ko'ring: Modelda
if (showNote)blokiniconst [count] = useState(1)qatoridan keyinga ko'chirsak, ikkinchi chizishdacountto'g'ri bo'ladimi?notechi?
Javob
count to'g'ri bo'ladi: u ikkala chizishda ham 2-chaqiruv, katak 1. note esa 3-chaqiruv bo'lib, yangi katak 2 ni oladi — "piyozsiz". Bu safar tasodifan to'g'ri ishladi. Lekin showNote yana false bo'lsa, katak 2 eslab qolinadi, keyin yana ochilganda eski qiymat qaytadi — xatti-harakat shartga bog'liq bo'lib qoladi. Qoida "ba'zan ishlaydi" uchun emas: hook har doim bir xil tartibda bo'lsa, umuman o'ylash shart emas.
2.3 Haqiqiy React: uch xil natija
Haqiqiy React modelimizdan aqlliroq: u har katakka hook turini ham yozib qo'yadi. Shuning uchun qoida buzilganda uch xil natija bo'ladi. Uchalasini jonli ko'ramiz.
Hook qo'shildi. Tugma bosilganda shart ochiladi va yangi useState paydo bo'ladi:
import { useState } from "react";
function App() {
const [editing, setEditing] = useState(false);
if (editing) {
const [text, setText] = useState("Osh");
}
return <button onClick={() => setEditing(true)}>Tahrirlash</button>;
}Konsolda:
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.Ikki xabar keldi. Birinchisi — ogohlantirish: "React App chaqirgan hooklar tartibi o'zgarganini aniqladi. Tuzatilmasa, bu xatolarga olib keladi." Ostidagi jadval juda foydali: chap ustun — oldingi chizish, o'ng — keyingisi. 2-qatorda oldin hech narsa (undefined) yo'q edi, endi useState bor. Ikkinchisi — xato: "Oldingi chizishdagidan ko'proq hook chizildi." Saytda «Komponentni chizishda xato» qutisi chiqadi, Maslahat qatorida esa — hooklar tartibi haqida eslatma.
Hook kamaydi. Erta return — Sardorning holati. Avval ikki hook chaqiriladi, tugmadan keyin esa bittasi:
import { useState } from "react";
function App() {
const [open, setOpen] = useState(true);
if (!open) return <p>Yopiq</p>;
const [price, setPrice] = useState(35000);
return <button onClick={() => setOpen(false)}>Yopish</button>;
}Konsolda:
Uncaught Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement."Kutilganidan kamroq hook chizildi. Bunga tasodifiy erta return sabab bo'lishi mumkin." React hatto sababni taxmin qilib aytyapti.
Tartib o'zgardi, soni o'sha. Bu eng xavflisi. Ikki useState o'rin almashsa, React turlarni solishtiradi — ikkalasi ham useState. Hech qanday xabar yo'q:
import { useState } from "react";
function App() {
const [swapped, setSwapped] = useState(false);
let dish, price;
if (swapped) {
[price] = useState(35000);
[dish] = useState("Osh");
} else {
[dish] = useState("Osh");
[price] = useState(35000);
}
return (
<div>
<p>Taom: {dish}</p>
<p>Narx: {price}</p>
<button onClick={() => setSwapped(true)}>Almashtir</button>
</div>
);
}Ekranda:
Taom: 35000
Narx: Osh
AlmashtirKonsol toza, ilova yiqilmadi — lekin taom nomi o'rnida narx turibdi. Bizning 20 qatorlik modelimiz bilan aynan bir xil xatti-harakat. Real ilovada bu "ba'zan noto'g'ri mijozga noto'g'ri narx" bo'lib chiqadi. Bunday xatoni topish soatlab vaqt oladi.
Diqqat: Uchinchi holatda React jim qoldi. Shuning uchun hooklar qoidasiga ish vaqtidagi xatoga tayanib emas, linter bilan rioya qilinadi — u kodni ishga tushirmasdan, yozayotgan paytingizda ushlaydi.
3. Ikki qoida
React hujjati ularni "Rules of Hooks" (hooklar qoidalari) deb ataydi. Ikkalasi ham «React hooklarni qanday eslaydi» bo'limidagi modeldan kelib chiqadi.
3.1 Birinchi qoida: faqat yuqori darajada
Hook komponent funksiyasining yuqori darajasida chaqiriladi — har qanday return dan oldin, hech qanday blok ichida emas. Har bir chizishda hamma hooklar bir xil sonda, bir xil tartibda chaqirilsin.
| Joy | Mumkinmi | Nega |
|---|---|---|
funksiya boshida, return dan oldin |
tartib doim bir xil | |
if / ternary / && ichida |
soni o'zgaradi | |
for / .map() ichida |
soni elementlar soniga bog'liq | |
erta return dan keyin |
ba'zan chaqirilmaydi | |
| hodisa ishlovchisi ichida | chizish paytida emas, bosilganda | |
useEffect ga berilgan funksiya ichida |
chizish tugagach chaqiriladi | |
try / catch ichida |
xato bo'lsa — keyingilari chaqirilmaydi |
Oxirgi uch qator haqida: hook — "chizish paytidagi" chaqiruv. React hisoblagichni faqat komponent chizilayotgan paytda yuritadi. Tugma bosilganda yoki effekt ishlaganda chizish allaqachon tugagan — hookning raqami yo'q.
3.2 Ikkinchi qoida: faqat React funksiyalaridan
Hook faqat ikki joydan chaqiriladi:
- Komponent — katta harf bilan boshlanadigan funksiya (Komponentlar darsidagi qoida).
- Custom hook —
usebilan boshlanadigan o'zingizning funksiyangiz (useCart,useOnline).
Oddiy yordamchi funksiya (formatPrice, calculateTotal) ichida hook chaqirib bo'lmaydi. Sabab — o'sha model: React hisoblagichi faqat komponent chizilayotgan paytda bor. Oddiy funksiyani esa istalgan joydan, istalgan payt chaqirish mumkin.
Custom hook (custom hook) — ichida boshqa hooklar chaqiradigan, nomi use bilan boshlanadigan funksiya. Nomdagi use — kelishuv emas, signal: linter faqat shu nom bo'yicha "bu hook" deb taniydi va unga ham qoidalarni qo'llaydi. O'z hooklarimizni Custom hooklar I darsida yozamiz. Hozircha shuni eslang: use bilan boshlanadigan nom faqat haqiqiy hooklarga beriladi.
Tekshirib ko'ring: Quyidagilardan qaysi biri qoidani buzadi? (a)
function Cart() { const [n] = useState(0); … }; (b)function getCart() { const [n] = useState(0); … }; (c)function useCart() { const [n] = useState(0); … }.
Javob
Faqat (b). Cart — katta harfli komponent, useCart — custom hook, ikkalasida ham hook mumkin. getCart — oddiy funksiya: ESLint React Hook "useState" is called in function "getCart" that is neither a React function component nor a custom React Hook function deydi.
4. ESLint qoidani qanday ushlaydi
4.1 Haqiqiy xabarlar
ESLint II darsida eslint-plugin-react-hooks ni ulagan edik. Uning rules-of-hooks qoidasi bugungi darsning qo'riqchisi. menu.jsx faylida uch xil buzilishni yozib, ESLint 10.12.0 va plagin 7.1.1 bilan tekshirdik:
import { useState } from "react";
export function Menu({ items }) {
for (const item of items) {
useState(item);
}
function handleClick() {
const [x] = useState(0);
}
return <button onClick={handleClick}>Menyu</button>;
}
function formatPrice(n) {
const [c] = useState(n);
return c;
}npx eslint menu.jsx chiqishi (har xabar oxiridagi qoida nomi react-hooks/rules-of-hooks qisqartirildi):
5:5 error React Hook "useState" may be executed more than once. Possibly because it is called in a loop. React Hooks must be called in the exact same order in every component render
8:17 error React Hook "useState" is called in function "handleClick" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"
14:15 error React Hook "useState" is called in function "formatPrice" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"
✖ 3 problems (3 errors, 0 warnings)Tarjimasi:
- 5-qator: "
useStatebir necha marta bajarilishi mumkin. Ehtimol, sikl ichida chaqirilgani uchun." - 8-qator: "
useStatehandleClickfunksiyasida chaqirilgan. U na komponent, na custom hook. Komponent nomi katta harf bilan, hook nomiusebilan boshlanishi shart." - 14-qator: xuddi shunday,
formatPriceuchun.
E'tibor bering: ESLint funksiya nomiga qaraydi. formatPrice ni useFormatPrice deb atasangiz, xato yo'qoladi — chunki endi u custom hook. Bu nomni "aldash" uchun emas: ichida haqiqatan hook bo'lgan funksiya hook bo'ladi va hook qoidalari unga ham tegishli.
4.2 TypeScript bu xatoni ko'rmaydi
vazifalar da Sardorning Tajriba.tsx faylini vaqtincha qo'shib, ikkala tekshiruvni ishga tushirdik:
npx eslint src/komponentlar/Tajriba.tsx
npx tsc -bNatija: ESLint — ✖ 1 problem (1 error, 0 warnings), exit 1. tsc -b — exit 0, jim. TypeScript turlarni tekshiradi, useState ning turi esa if ichida ham, tashqarisida ham bir xil. Qaysi tartibda chaqirilgani — tur emas, xatti-harakat. Shu sababli React loyihasida ESLint ixtiyoriy emas.
Maslahat:
vazifalardanpm run lint—--max-warnings=0bilan (Birinchi React loyiha darsida qo'shdik).rules-of-hooks—error, demak husky va CI uni o'tkazib yubormaydi. Keyingi darslarda tanishadiganexhaustive-depsesawarndarajasida.--max-warnings=0bo'lmaganda ogohlantirish CI'da jim o'tib ketardi.
5. Shartli hookni to'g'ri yozish
Qoida "shart ishlatma" demaydi. U "shartni hookdan keyin qo'y" deydi. Uchta usul bor.
5.1 Hookni yuqoriga, shartni pastga
Sardorning komponenti. Hook birinchi, erta return — keyin:
import { useState } from "react";
function EditRow({ editing }: { editing: boolean }) {
const [text, setText] = useState("Osh"); // har doim chaqiriladi
if (!editing) {
return null; // shart — hookdan keyin
}
return (
<input value={text} onChange={(e) => setText(e.target.value)} />
);
}
export default function App() {
return (
<div>
<EditRow editing={false} />
<EditRow editing={true} />
</div>
);
}Hech qanday ogohlantirish yo'q. editing false bo'lganda ham useState chaqiriladi — bu isrof emas. Bitta katakni o'qish juda arzon.
5.2 Shartni komponentga aylantirish
Ko'pincha undan ham toza yo'l bor: shart ostidagi qismni alohida komponent qilish. Hook o'sha komponentning yuqori darajasiga ko'chadi, shart esa ota'da qoladi:
import { useState } from "react";
function NoteField() {
const [note, setNote] = useState("piyozsiz");
return <p>Izoh: {note}</p>;
}
function App() {
const [showNote, setShowNote] = useState(false);
return (
<div>
<p>Osh × 1</p>
{showNote && <NoteField />}
<button onClick={() => setShowNote(true)}>Izoh qo'shish</button>
</div>
);
}Shartli komponent — mutlaqo qonuniy. NoteField chizilmasa, uning butun xotirasi o'chadi. Qayta paydo bo'lsa — yangidan boshlanadi. Har komponentning o'z garderobi bor, raqamlar aralashmaydi. Komponent qachon xotirasini saqlashi va qachon yo'qotishi — State'ning saqlanishi va key bilan reset darsida.
5.3 Ro'yxat uchun — massiv bitta state'da
Sikl ichida useState o'rniga — butun ro'yxat bitta state'da (Obyekt va massivlarni immutable yangilash) yoki har element o'z komponentida (.map() + key bilan <CartRow />). Ikkinchi usulda har CartRow ichida o'z useState i bor — bu ham qonuniy, chunki har komponent nusxasi alohida xotira.
Tekshirib ko'ring:
items.map((item) => <CartRow key={item.id} item={item} />)— bu yerda ham hook sikl ichida chaqirilmayaptimi?CartRowichidauseStatebor-ku.
Javob
Yo'q. .map() hookni emas, elementlarni yaratadi (<CartRow /> — oddiy obyekt). Hook CartRow funksiyasining ichida, uning yuqori darajasida chaqiriladi — va har CartRow nusxasining o'z kataklari bor. Hook chaqiradigan funksiya App emas, CartRow.
6. use() — yagona istisno
React 19 da use nomli funksiya qo'shildi. U ikki narsani o'qiydi: Context (komponentlar daraxti bo'ylab uzatiladigan qiymat — Context I darsida) va Promise (use(): promise va context'ni o'qish darsida). Hozir ularni bilish shart emas — faqat bitta qiziq xususiyatni ko'ramiz. use ni shart ichida chaqirish mumkin:
import { createContext, use, useState } from "react";
const ThemeContext = createContext("yorug'");
function App() {
const [showTheme] = useState(true);
let theme = "—";
if (showTheme) {
theme = use(ThemeContext); // ✅ use() — shart ichida ham mumkin
}
return <p>Mavzu: {theme}</p>;
}Ekranda:
Mavzu: yorug'ESLint bu faylda jim. Nega use boshqalardan farq qiladi? U komponent xotirasida katak egallamaydi. U har chaqirilganda qiymatni tashqaridan — Context'dan yoki Promise'dan — yangidan o'qiydi. Garderobga palto topshirmaydi, demak raqamlar siljimaydi. Shuning uchun nomi ham useUse emas, shunchaki use.
Lekin use ham faqat komponent yoki hook ichida chaqiriladi (ikkinchi qoida unga ham tegishli). try/catch ichida esa uni chaqirib bo'lmaydi.
7. Vazifalar o'lchovi: ESLint va TypeScript
Bu dars vazifalar kodini o'zgartirmaydi — kanonda hooklar qoidasi allaqachon bajarilgan. O'rniga bitta tajriba qildik: Sardorning komponentini src/komponentlar/Tajriba.tsx ga vaqtincha qo'yib, ikki tekshiruvchini taqqosladik (haqiqiy chiqish):
$ npx eslint src/komponentlar/Tajriba.tsx
7:27 error React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return? react-hooks/rules-of-hooks
✖ 1 problem (1 error, 0 warnings)
exit 1
$ npx tsc -b
exit 07:27 — 7-qator, 27-ustun: aynan useState so'zi. Keyin fayl o'chirildi. Xulosa vazifalar uchun: npm run check (lint + format + tip + test) hooklar qoidasini ham qo'riqlaydi, tip yolg'iz — yo'q.
Kanondagi hooklarni ham sanadik. Holat 24 da 5 ta komponentda 7 ta useState bor — hammasi funksiya boshida. Keyingi darslarda App ning o'ziga state, effekt va ref'lar qo'shiladi: useRef II darsi oxirida komponentlardagi hook chaqiruvlari 30 dan oshadi. Har birini qo'shganda birinchi savol: "u return dan oldin va hech qanday shart ichida emasmi?"
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Rendered more hooks than during the previous render. |
if ichidagi hook ochildi |
hookni yuqoriga, shartni pastga |
Rendered fewer hooks than expected. |
erta return hookdan oldin |
return ni hamma hooklardan keyinga |
change in the order of Hooks + jadval |
turli shartda turli hooklar | jadvaldagi ^^^^ qatoriga qarang |
| qiymatlar jim almashgan | bir xil turdagi hooklar o'rin almashgan | ESLint ni yoqing — ish vaqtida xabar bo'lmaydi |
is called in function "…" that is neither… |
oddiy funksiya yoki ishlovchida hook | komponentga ko'chiring yoki use… custom hook qiling |
may be executed more than once |
sikl ichida hook | massiv — bitta state'da, yoki har element o'z komponentida |
Yana bitta tuzoq — nomni aldash. ESLint xatosini yo'qotish uchun formatPrice ni useFormatPrice ga o'zgartirish. Funksiya endi "hook" bo'ldi va uni ham faqat yuqori darajada chaqirish kerak. Agar siz uni .map() ichida chaqirayotgan bo'lsangiz, muammo yo'qolmadi — faqat bir qavat chuqurroqqa ko'chdi.
9. Mashqlar
Mashqlar kurs/mashqlar/17/25-hooklar-qoidasi/ da. Har yechimni «Ishga tushir» bilan sinang.
1-mashq (oson): qaysi biri buzadi?
Har funksiyani o'qing va javob bering: qoida buziladimi?
function Price({ sale }) {
const [price] = useState(35000);
return <p>{sale ? price * 0.9 : price}</p>;
}
function Badge({ isNew }) {
if (isNew) {
const [label] = useState("Yangi");
return <b>{label}</b>;
}
return null;
}
function useDiscount() {
const [percent] = useState(10);
return percent;
}Priceqoidani buzadimi (ha/yo'q):Badgeqoidani buzadimi (ha/yo'q):useDiscountqoidani buzadimi (ha/yo'q):
Yechim
Price — hook yuqori darajada, shart faqat qiymatni tanlaydi. Badge — useState if ichida: isNew o'zgarsa, hooklar soni 0 ↔ 1 almashadi. useDiscount — use bilan boshlanadi, demak custom hook; ichidagi useState yuqori darajada.
2-mashq (o'rta): Sardorning tahrir qatorini tuzating
Kod tugma bosilganda yiqiladi. Uni tuzating: useState bitta joyda, tahrir yopiq bo'lsa — Tahrirlash tugmasi, ochiq bo'lsa — maydon va Saqlash tugmasi. Hookni shart ichidan chiqarish uchun «Hookni yuqoriga, shartni pastga» bo'limiga qarang.
import { useState } from "react";
function App() {
const [editing, setEditing] = useState(false);
if (!editing) {
return (
<button onClick={() => setEditing(true)}>Tahrirlash</button>
);
}
const [text, setText] = useState("Non olish");
return (
<div>
<input value={text} onChange={(e) => setText(e.target.value)} />
<button onClick={() => setEditing(false)}>Saqlash</button>
</div>
);
}Konsolda:
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.Yechim
import { useState } from "react";
function App() {
const [editing, setEditing] = useState(false);
const [text, setText] = useState("Non olish"); // har doim
if (!editing) {
return (
<button onClick={() => setEditing(true)}>Tahrirlash</button>
);
}
return (
<div>
<input value={text} onChange={(e) => setText(e.target.value)} />
<button onClick={() => setEditing(false)}>Saqlash</button>
</div>
);
}Ekranda:
SaqlashTugma bosildi, maydon va «Saqlash» paydo bo'ldi (maydon qiymati Ekranda ga kirmaydi). text endi tahrir yopiq bo'lganda ham saqlanadi — qayta ochsangiz, oldingi matn turadi. Bu ko'pincha aynan kerakli xatti-harakat.
3-mashq (qiyin): modelga qo'riqchi qo'shing
«O'zimiz yasab ko'ramiz» bo'limidagi modelni kengaytiring: React kabi har chizishda hooklar sonini tekshirsin. render oxirida:
- agar oldingi chizishda hooklar ko'proq bo'lsa —
Error("Rendered fewer hooks than expected.")tashlasin; - kamroq bo'lsa (va bu birinchi chizish bo'lmasa) —
Error("Rendered more hooks than during the previous render.").
Ishora: oldingi chizishdagi sonni let lastCount = null da saqlang; index chizish oxirida aynan shu chizishdagi hooklar soni. try/catch bilan xatoni ushlab, xato.message ni chiqaring (Xatolarni ushlash).
Yechim
const slots = [];
let index = 0;
let lastCount = null; // oldingi chizishdagi hooklar soni
const FEWER = "Rendered fewer hooks than expected.";
const MORE = "Rendered more hooks than during the previous render.";
function useState(initial) {
const i = index;
index++;
if (slots.length <= i) slots.push(initial);
return [slots[i], (value) => { slots[i] = value; }];
}
function render(component, props) {
index = 0;
const result = component(props);
if (lastCount !== null && index < lastCount) {
throw new Error(FEWER);
}
if (lastCount !== null && index > lastCount) {
throw new Error(MORE);
}
lastCount = index;
return result;
}
function OrderCard({ showNote }) {
const [dish] = useState("Osh");
let note = "";
if (showNote) {
[note] = useState("piyozsiz"); // ❌ ataylab
}
return `${dish}${note ? ", " + note : ""}`;
}
for (const showNote of [false, false, true]) {
try {
console.log(render(OrderCard, { showNote }));
} catch (xato) {
console.log(`Xato: ${xato.message}`);
}
}Konsolda:
Osh
Osh
Xato: Rendered more hooks than during the previous render.Birinchi chizishda lastCount hali null — tekshiruv yo'q, son eslab qolinadi (1). Ikkinchisida 1 = 1. Uchinchisida 2 > 1 — xato. Haqiqiy React shu tekshiruvni qiladi va qo'shimcha ravishda har katakdagi hook turini solishtiradi — «Haqiqiy React: uch xil natija» bo'limidagi jadval shundan.
10. Real ishda
- Har React loyihada
eslint-plugin-react-hooksbor: Vite shabloni, Next.js (eslint-config-next) — hammasidarules-of-hookssukut bo'yichaerror. Uni o'chirish — kod ko'rigida (code review) darhol savol tug'diradigan narsa. - Kod ko'rigida ko'p uchraydigan holat: kimdir tez tuzatish uchun hookni
ifichiga qo'yib, ESLint qatorini// eslint-disable-next-linebilan o'chiradi. Bunday qatorni ko'rsangiz — to'xtating. - React Compiler (avtomatik optimallashtiruvchi, React Compiler darsida) qoidalar buzilgan komponentni optimallashtirmaydi. Plagin 7 dagi kompilyator qoidalari ham shu sababli
recommendedichida. - Intervyu savollari: "Nega hookni
ifichida chaqirib bo'lmaydi?" (javob: chaqiruv tartibi), "React state'ni qanday qilib to'g'ri komponentga bog'laydi?", "use()nega shart ichida chaqirilishi mumkin?".
Xulosa
- React hooklarni nomi bo'yicha emas, chaqirilish tartibi bo'yicha eslaydi — har komponentning kataklar ro'yxati bor.
- Birinchi qoida: hook faqat yuqori darajada —
if, sikl, ertareturndan keyin, ishlovchi vatryichida emas. - Ikkinchi qoida: hook faqat komponent yoki
use…custom hook ichida. - Buzilish:
Rendered more/fewer hooksxatosi, "tartib o'zgardi" jadvali yoki — eng xavflisi — qiymatlarning jim almashishi. - Ushlovchi — ESLint
react-hooks/rules-of-hooks; TypeScript bu xatoni ko'rmaydi (tsc -b—exit 0). - To'g'ri shakl: hook yuqorida, shart pastda yoki shartli qism alohida komponentda.
use()— yagona istisno.
Keyingi dars: useEffect I: effekt — tashqi tizim bilan sinxronlash — yangi hook: chizish tugagach ishlaydigan kod va u nega "tashqi tizim" uchun kerak.
Manbalar
- React: "Rules of Hooks" — react.dev/reference/rules/rules-of-hooks
- React:
use— react.dev/reference/react/use - React: "Reusing Logic with Custom Hooks" (
useprefiksi) — react.dev/learn/reusing-logic-with-custom-hooks eslint-plugin-react-hooks7.1.1 — npmjs.com/package/eslint-plugin-react-hooks
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!