Mundarija (28)
- Bu darsda
- 1. Nega bu kerak?
- 2. Element — oddiy obyekt
- 2.1 JSX nima qaytaradi
- 2.2 "Virtual DOM" — atama haqida
- 3. Diffing: ikki qoida
- 3.1 Nega qoida kerak
- 3.2 Turi o'zgarsa — hammasi yangidan
- 4. Key: bolalarni juftlash
- 4.1 Indeks-key bilan
- 4.2 Barqaror key bilan
- 4.3 vazifalar dagi key
- 5. Fiber: React'ning ish daftari
- 5.1 Element — bir martalik, Fiber — doimiy
- 5.2 Haqiqiy Fiber daraxtini aylanamiz
- 5.3 Ikki daraxt: current va alternate
- 6. Work loop va lanes
- 6.1 Ish sikli
- 6.2 Lanes: ustuvorlik
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson): saqlanadimi?
- 2-mashq (o'rta): saralashdagi tuzoq
- 3-mashq (qiyin): bir xil tur, boshqa key
- 4-mashq: vazifalar da key'ni buzib ko'ring
- 9. Real ishda
- Xulosa
- Manbalar
Reconciliation, Virtual DOM va Fiber: React ikki daraxtni qanday solishtiradi
Qisqacha: Render natijasi — DOM emas, oddiy JavaScript obyektlari daraxti (
{ type: "p", props: {…} }), ko'pincha "Virtual DOM" deyiladi. Reconciliation (moslashtirish) — React yangi daraxtni eskisi bilan solishtirib, DOM'dagi eng kichik o'zgarishni topishi. U ikki qoidaga tayanadi: turi o'zgargan element — butunlay yangi (eski DOM va state o'chadi), ro'yxatdagi bolalar esakeybo'yicha juftlanadi. Ichkarida har komponent — Fiber degan ish birligi; React ularni sikl bilan bittalab ishlaydi va orada to'xtay oladi. Ro'yxat boshiga bitta taom qo'shilganda indeks-key bilan 4 ta DOM o'zgarishi,key={nomi}bilan — 1 ta: biz buni o'lchadik.
Bu darsda
- JSX element nima ekanini konsolda ko'rasiz va "Virtual DOM" ni to'g'ri tushunasiz.
- Diffing'ning ikki qoidasini (tur va
key) va ularning oqibatlarini bilasiz. - Indeks-key va barqaror key farqini DOM o'zgarishlari soni bilan o'lchaysiz.
- Fiber daraxtini (
child,sibling,return) haqiqiy ilovada aylanib chiqasiz. - Work loop va lanes (ustuvorlik) g'oyasini umumiy holda tushuntira olasiz.
Oldin bilishingiz kerak: Render va commit, Ro'yxatlarni render qilish va key, State'ning saqlanishi va key bilan reset, Concurrent rendering va useTransition.
1. Nega bu kerak?
Render va commit darsida «Yangila» tugmasi App ni qayta chizdi, lekin DOM'da 0 ta o'zgarish bo'ldi. «Narx» esa uchta matn tugunidan faqat bittasini almashtirdi. React buni qanday biladi? U qayerdan "faqat shu tugun o'zgardi" degan xulosaga keladi?
Bu savolning javobi kundalik xatolarni tushuntiradi. Nega ro'yxatda key={index} bo'lsa, birinchi qatorga yozilgan matn boshqa qatorga "ko'chib" ketadi (Ro'yxatlar va key)? Nega <div> ni <section> ga almashtirsangiz, ichidagi hisoblagich nolga tushadi (State'ning saqlanishi)? Nega komponent ichida e'lon qilingan komponent har bosishda fokusni yo'qotadi? Uchalasi — bitta mexanizmning oqibati.
Hayotiy o'xshatish: Jasur aka kechagi va bugungi menyu taxtasini solishtiradi. U har harfni solishtirmaydi. Qoidasi oddiy: "bo'lim nomi o'zgarmagan bo'lsa — ichiga qarayman; o'zgargan bo'lsa — butun bo'limni o'chirib, yangidan yozaman". Taomlarni esa nomi bo'yicha juftlaydi, tartib raqami bo'yicha emas. React ham shunday.
2. Element — oddiy obyekt
2.1 JSX nima qaytaradi
JSX I darsida JSX jsx() chaqiruviga aylanishini ko'rgansiz. Chaqiruv natijasini konsolga chiqaramiz:
function MenuCard({ dish }) {
return <h2>{dish}</h2>;
}
const price = <p className="narx">35000</p>;
const card = <MenuCard dish="Osh" />;
console.log(typeof price, String(price.$$typeof));
console.log(price.type, JSON.stringify(price.props), price.key);
console.log(card.type === MenuCard, JSON.stringify(card.props));
function App() {
return price;
}Konsolda:
object Symbol(react.transitional.element)
p {"className":"narx","children":"35000"} null
true {"dish":"Osh"}React element — "ekranda shu bo'lsin" degan tavsif obyekti. Unda type (teg nomi satr sifatida yoki komponent funksiyasining o'zi), props (atributlar va children) va key bor. $$typeof — React 19 dagi belgi (Symbol), React buni o'z elementi deb taniydi (JSX II darsidagi xavfsizlik tafsiloti). Ikkinchi elementning type i — MenuCard funksiyasining o'zi: React uni hali chaqirmagan.
2.2 "Virtual DOM" — atama haqida
Komponentlar qaytargan elementlar daraxtini ko'pincha Virtual DOM deyishadi: haqiqiy DOM'ning yengil, xotiradagi "nusxasi". Bu atama 2013–2018 yillarda keng tarqaldi, hozirgi react.dev hujjatlari esa uni deyarli ishlatmaydi — "UI daraxti", "render daraxti" deydi.
Bir mifni yo'qotamiz: Virtual DOM DOM'dan "tezroq" emas. Har o'zgarishni qo'lda, aniq DOM chaqiruvi bilan yozgan dastur React'dan tez ishlaydi — React avval solishtirishga vaqt sarflaydi. Virtual DOM'ning foydasi boshqa: siz butun ekranni "holatga qarab" deklarativ yozasiz (React nima), React esa minimal o'zgarishni o'zi topadi. Svelte va Solid kabi kutubxonalar Virtual DOM'siz ishlaydi — ular o'zgarishni kompilyatsiya paytida aniqlaydi.
3. Diffing: ikki qoida
3.1 Nega qoida kerak
Ikki ixtiyoriy daraxtni solishtirib, eng kam amal bilan birini ikkinchisiga aylantirish — og'ir masala: ma'lum algoritmlar n ta element uchun taxminan n³ amal qiladi. 1 000 elementda — milliard amal, har bosishda. React bunday qilmaydi. U ikki taxminga tayanib, ishni n ga tushiradi (har element bir marta ko'riladi):
- Turi boshqa element — boshqa daraxt.
<div>o'rniga<section>yokiHeadero'rnigaFooterkelsa, React ichiga qaramaydi: eskisini butunlay o'chiradi, yangisini noldan yaratadi. key— bolalarning ismi. Bir ota ichidagi bolalarkeybo'yicha juftlanadi;keybo'lmasa — tartib raqami (indeks) bo'yicha.
Taxmin ba'zan "noto'g'ri" bo'lishi mumkin (masalan, <div> → <section> ichidagi hamma narsa aslida o'sha). Lekin amalda bunday holat kam, tezlik esa ming barobar farq qiladi.
flowchart TD
A["Eski va yangi element"] --> B{"type bir xilmi?"}
B -->|"yo'q"| C["Eskisini o'chir, yangisini yarat"]
B -->|"ha"| D["DOM va state saqlanadi"]
D --> E["props farqini DOM'ga yoz"]
E --> F["Bolalarni key bo'yicha juftla"]
F --> ADiagrammadagi halqaga qarang: bolalar uchun ham xuddi shu savol beriladi — daraxt pastga qarab bir marta aylanib chiqiladi.
3.2 Turi o'zgarsa — hammasi yangidan
State'ning saqlanishi va key bilan reset darsidagi qoida aynan shu: state komponentga emas, daraxtdagi o'rniga bog'langan. O'rin o'sha, tur boshqa — eski komponent "o'ladi". Bitta klassik tuzoq bunga yangi rang qo'shadi — komponent ichida e'lon qilingan komponent:
import { useEffect, useState } from "react";
function App() {
const [tick, setTick] = useState(0);
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("Counter yaratildi");
}, []);
return <button onClick={() => setCount(count + 1)}>+1</button>;
}
return (
<div>
<Counter />
<button onClick={() => setTick(tick + 1)}>Yangila</button>
</div>
);
}Konsolda:
Counter yaratildi
Counter yaratildiIkkinchi qator — «Yangila» dan keyin. App qayta chizilganda function Counter yangidan e'lon qilinadi — bu yangi funksiya obyekti. React uchun type o'zgardi: oldingiCounter !== yangiCounter. Birinchi qoida ishladi — eski Counter o'chirildi (state bilan birga), yangisi yaratildi. Agar ichida input bo'lsa, har renderda fokus va yozilgan matn yo'qolardi. Davosi — komponentni modul darajasida, tashqarida e'lon qilish. ESLint buni react-hooks/static-components qoidasi bilan ushlaydi (React Compiler darsida bu qoidalar to'plamini ko'ramiz) — lazy darsida vazifalar da aynan shu xato topilgan edi.
Tekshirib ko'ring:
{isAdmin ? <Panel /> : <Panel />}—isAdmino'zgarsa,Panelning state'i saqlanadimi?
Javob
Saqlanadi. Ikkala shoxda ham o'sha joyda o'sha type (Panel) turibdi — React uchun bu bir komponent, faqat props boshqa bo'lishi mumkin. Agar ataylab qayta yaratish kerak bo'lsa — har shoxga boshqa key bering: <Panel key="admin" /> va <Panel key="mehmon" /> (State'ning saqlanishi).
4. Key: bolalarni juftlash
4.1 Indeks-key bilan
Endi ikkinchi qoida — o'lchov bilan. Uch taomli ro'yxat, «Boshiga qo'sh» — boshiga "Lag'mon" qo'shadi. Render va commit dagi MutationObserver usuli bilan DOM o'zgarishlarini sanaymiz. Avval key={i}:
import { useLayoutEffect, useRef, useState } from "react";
const observer = new MutationObserver(() => {});
function App() {
const [dishes, setDishes] = useState(["Osh", "Manti", "Somsa"]);
const box = useRef(null);
useLayoutEffect(() => {
observer.observe(box.current, {
subtree: true,
childList: true,
characterData: true,
});
return () => observer.disconnect();
}, []);
useLayoutEffect(() => {
const types = observer.takeRecords().map((r) => r.type);
const list = types.join(", ") || "—";
console.log("o'zgarish:", types.length, list);
});
function addFirst() {
setDishes(["Lag'mon", ...dishes]);
}
return (
<div>
<button onClick={addFirst}>Boshiga qo'sh</button>
<ul ref={box}>
{dishes.map((d, i) => (
<li key={i}>{d}</li>
))}
</ul>
</div>
);
}Konsolning ikkinchi qatoriga qarang: bitta taom qo'shildi, lekin o'zgarishlar soni nechta?
Konsolda:
o'zgarish: 0 —
o'zgarish: 4 characterData, characterData, characterData, childListReact ro'yxatlarni key bo'yicha juftladi: eski key=0 ("Osh") ↔ yangi key=0 ("Lag'mon"). Turi bir xil (li), demak element saqlanadi, faqat matn o'zgaradi. Shunday qilib uchta mavjud <li> ning uchalasida matn almashdi (3 × characterData), oxiriga esa yangi <li> qo'shildi (childList, matni "Somsa").
key |
Eski matn | Yangi matn | DOM'da |
|---|---|---|---|
| 0 | Osh | Lag'mon | matn almashdi |
| 1 | Manti | Osh | matn almashdi |
| 2 | Somsa | Manti | matn almashdi |
| 3 | — | Somsa | yangi <li> |
4.2 Barqaror key bilan
Bitta o'zgarish — key={d} (taom nomi takrorlanmaydi deb hisoblaymiz):
import { useLayoutEffect, useRef, useState } from "react";
const observer = new MutationObserver(() => {});
function App() {
const [dishes, setDishes] = useState(["Osh", "Manti", "Somsa"]);
const box = useRef(null);
useLayoutEffect(() => {
observer.observe(box.current, {
subtree: true,
childList: true,
characterData: true,
});
return () => observer.disconnect();
}, []);
useLayoutEffect(() => {
const types = observer.takeRecords().map((r) => r.type);
const list = types.join(", ") || "—";
console.log("o'zgarish:", types.length, list);
});
function addFirst() {
setDishes(["Lag'mon", ...dishes]);
}
return (
<div>
<button onClick={addFirst}>Boshiga qo'sh</button>
<ul ref={box}>
{dishes.map((d) => (
<li key={d}>{d}</li>
))}
</ul>
</div>
);
}Konsolda:
o'zgarish: 0 —
o'zgarish: 1 childListEndi React "Osh", "Manti", "Somsa" ni o'z key lari bo'yicha topdi — ular o'zgarmagan. Faqat key="Lag'mon" yangi: bitta <li> yaratilib, ro'yxat boshiga qo'yildi. Bitta DOM amali.
key={i} |
key={d} |
|
|---|---|---|
| DOM o'zgarishi | 4 (3 matn + 1 element) | 1 (1 element) |
eski <li> lar |
o'sha, lekin boshqa matn bilan | o'sha, o'sha matn bilan |
<li> ichida input bo'lsa |
yozilgan matn boshqa taomga "ko'chadi" | to'g'ri joyida |
Uchinchi qator — eng xavflisi. Ro'yxat qatorida input, katak yoki ochiq menyu bo'lsa, indeks-key bilan ularning holati boshqa ma'lumotga yopishib qoladi: React DOM elementini saqlaydi, lekin unga boshqa taomni yozadi. Ma'lumotda barqaror id bo'lsa — doim shuni ishlating. vazifalar da aynan shunday: key={vazifa.id}.
Tekshirib ko'ring: Ro'yxat oxiriga taom qo'shilsa,
key={i}bilan nechta DOM o'zgarishi bo'ladi?
Javob
Bitta. Eski key=0,1,2 yangi ro'yxatda ham o'sha taomlarga to'g'ri keladi — matn o'zgarmaydi, faqat key=3 yangi. Indeks-key faqat boshiga qo'shish, o'rtadan o'chirish va saralash da muammo beradi. Shuning uchun xato ko'pincha yashirin qoladi: sinovda faqat oxiriga qo'shiladi.
4.3 vazifalar dagi key
Kanondagi ToliqRoyxat (holat 23) izohida shu dars yozilgan:
function ToliqRoyxat({ vazifalar, sorov, tahrirId }: Props) {
// key — vazifa id: barqaror va takrorlanmaydi. Tartib
// o'zgarsa (saralash, bajarilgan pastga tushdi) React <li> ni
// yangidan yasamaydi, joyini almashtiradi — fokus va yozilgan
// matn saqlanadi
return vazifalar.map((vazifa) => (
<VazifaQatori
key={vazifa.id}Kanon izohi telefonga sig'ishi uchun qayta bo'lindi, matni o'sha. Bajarilgan vazifa ro'yxat pastiga tushadi (Mini-ilova). key={vazifa.id} bilan React o'sha <li> ni ko'chiradi — Bobur Space bosgan katakda fokus qoladi (Fokus boshqaruvi darsida e2e tekshiruvi).
Birinchi qoidaning ham vazifalar dagi izi bor. Holat 23 da VazifaRoyxati vazifalar soniga qarab ikki xil komponent qaytaradi: 200 tagacha — <ToliqRoyxat>, ko'p bo'lsa — <VirtualRoyxat> (Virtualizatsiya). Shu chegaradan o'tilganda o'sha joyda type o'zgaradi — React eski ro'yxatni butunlay o'chirib, yangisini yaratadi. Bu ataylab: ikki xil hooklar to'plami bitta komponentda shart bilan chaqirilmaydi (Hooklar qoidalari), chegaradan o'tish esa kam bo'ladi.
5. Fiber: React'ning ish daftari
5.1 Element — bir martalik, Fiber — doimiy
Element har renderda yangidan yasaladi va keyin tashlanadi. React esa renderlar orasida ko'p narsani eslab qolishi kerak: state, effektlar, DOM tuguni, "kim kimning otasi". Buning uchun u har komponent va har DOM elementi uchun Fiber obyektini saqlaydi. Fiber (tola) — React 16 dan (2017) beri ichki tuzilma: bitta daraxt tuguni va shu bilan birga bitta ish birligi (unit of work).
Fiber'ning asosiy maydonlari:
| Maydon | Nima |
|---|---|
type, key |
elementdan: teg yoki komponent funksiyasi |
child, sibling, return |
birinchi bola, keyingi aka-uka, ota |
memoizedState, memoizedProps |
oxirgi commit'dagi state (hooklar ro'yxati) va props |
stateNode |
DOM tuguni (teg uchun) |
alternate |
shu Fiber'ning ikkinchi nusxasi |
flags, lanes |
nima qilish kerak va qanchalik shoshilinch |
5.2 Haqiqiy Fiber daraxtini aylanamiz
Fiber — ichki tuzilma, hujjatlanmagan va versiyadan versiyaga o'zgaradi. Lekin React DevTools uni o'qiydi, biz ham o'rganish uchun o'qiymiz. react-dom har DOM tuguniga __reactFiber$… nomli xususiyat qo'shadi (oxiri tasodifiy belgilar):
import { useLayoutEffect } from "react";
function fiberOf(node) {
const key = Object.keys(node).find((k) =>
k.startsWith("__reactFiber$"),
);
return node[key];
}
function nameOf(fiber) {
if (typeof fiber.type === "string") return `<${fiber.type}>`;
if (typeof fiber.type === "function") return fiber.type.name;
return `(tag ${fiber.tag})`;
}
function Header() {
return <h1>«Bahor»</h1>;
}
function Dish({ title }) {
return <li id={title}>{title}</li>;
}
function App() {
useLayoutEffect(() => {
const path = [];
let f = fiberOf(document.getElementById("Manti"));
while (f) {
path.push(nameOf(f));
f = f.return;
}
console.log("return:", path.join(" → "));
const list = fiberOf(document.querySelector("ul"));
const kids = [];
for (let c = list.child; c; c = c.sibling) kids.push(nameOf(c));
console.log("<ul> bolalari:", kids.join(", "));
}, []);
return (
<main>
<Header />
<ul>
<Dish title="Osh" />
<Dish title="Manti" />
</ul>
</main>
);
}Konsolda:
return: <li> → Dish → <ul> → <main> → App → (tag 3)
<ul> bolalari: Dish, DishBirinchi qator — "Manti" <li> idan yuqoriga, return (ota) bo'ylab. Dish — alohida tugun: komponentning o'zi ham Fiber, uning ichida <li>. Eng yuqoridagi (tag 3) — HostRoot, createRoot yaratgan ildiz. Ikkinchi qator — <ul> ning bolalari: child (birinchisi), keyin sibling zanjiri. Bu daraxt rasmda:
flowchart TD
R["HostRoot"] -->|"child"| A["App"]
A -->|"child"| M["main"]
M -->|"child"| H["Header"]
H -->|"sibling"| U["ul"]
H -->|"child"| H1["h1"]
U -->|"child"| D1["Dish: Osh"]
D1 -->|"sibling"| D2["Dish: Manti"]
D1 -->|"child"| L1["li"]
D2 -->|"child"| L2["li"]Strelkalarga qarang: ota faqat birinchi bolasini biladi (child), qolganlari zanjir (sibling). Bu massiv emas — bog'langan ro'yxat. Nega shunday — keyingi bo'limda. vazifalar dagi render o'lchov skripti (Render va commit dagi onCommitFiberRoot) aynan shu maydonlar bo'yicha yurgan edi: root.current → child → sibling.
Diqqat:
__reactFiber$— o'rganish va o'lchov vositalari uchun. Ilova kodida unga tayanmang: React yangilanishida u ogohlantirishsiz o'zgarishi mumkin. Komponent ma'lumoti kerak bo'lsa — props, context yoki ref.
5.3 Ikki daraxt: current va alternate
React bir vaqtda ikki Fiber daraxtini saqlaydi. current — ekrandagi holat (oxirgi commit). work-in-progress — hozir hisoblanayotgan yangi holat. Har Fiber'ning alternate i — ikkinchi daraxtdagi jufti. Commit — oddiy almashtirish: ildizning current ko'rsatkichi yangi daraxtga o'tadi, eskisi keyingi render uchun qoralama bo'ladi.
Bu double buffering (ikki buferli chizish) — o'yinlardagi usul: keyingi kadr ko'rinmas buferda chiziladi, tayyor bo'lgach bir lahzada almashtiriladi. Shuning uchun ekranda yarim tayyor holat ko'rinmaydi, tashlab yuborilgan render esa hech narsani buzmaydi — u faqat qoralamada edi.
6. Work loop va lanes
6.1 Ish sikli
Fiber'lar bog'langan ro'yxat bo'lgani uchun React daraxtni rekursiyasiz, oddiy sikl bilan aylana oladi. Soddalashtirilgan ko'rinishi (React kodining g'oyasi, aynan kodi emas):
function workLoopConcurrent() {
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress); // bitta Fiber
}
}
function performUnitOfWork(fiber) {
// komponentni chaqir, bolalarni solishtir
const next = beginWork(fiber);
if (next !== null) {
workInProgress = next; // pastga: child
} else {
completeUnitOfWork(fiber); // sibling yoki yuqoriga: return
}
}Work loop (ish sikli) — "keyingi Fiber'ni ol, ishla, keyingisiga o't". beginWork — komponent funksiyasini chaqiradi va yangi bolalarni eskilari bilan solishtiradi (reconciliation shu yerda). Bola bo'lmasa — sibling ga, u ham bo'lmasa — return bo'ylab yuqoriga. Rekursiya bo'lsa, chaqiruvlar stekini o'rtada to'xtatib bo'lmasdi. Sikl esa istalgan Fiber'dan keyin to'xtay oladi va keyinroq workInProgress dan davom etadi.
shouldYield() — Concurrent rendering darsidagi "5 ms o'tdimi, navbatda bosish bormi?" savoli. Transition renderi aynan shu siklda bo'laklarga bo'linadi. Shoshilinch render esa shouldYield siz siklda (workLoopSync) — oxirigacha.
6.2 Lanes: ustuvorlik
Lane (yo'lak) — yangilanishning ustuvorligi, bitlar bilan yozilgan son. Har yangilanish o'z yo'lagiga tushadi; bir nechta yo'lak bitta songa | bilan yig'iladi (Bit amallari). React avval eng muhim yo'lakni ishlaydi. Natija oynasida ildizdagi kutilayotgan yo'laklarni o'qiymiz. pendingLanes — yana ichki maydon: yuqoridagi «Diqqat» bloki bu yerda ham amal qiladi, faqat o'rganish uchun.
import { startTransition, useState } from "react";
function pendingLanes() {
const el = document.getElementById("root");
const key = Object.keys(el).find((k) =>
k.startsWith("__reactContainer$"),
);
return el[key].stateNode.pendingLanes;
}
function App() {
const [tab, setTab] = useState("bosh");
function openMenu() {
startTransition(() => setTab("taomlar"));
console.log("transition:", pendingLanes());
}
function openContact() {
setTab("aloqa");
console.log("bosish:", pendingLanes());
}
return (
<div>
<button onClick={openMenu}>Taomlar</button>
<button onClick={openContact}>Aloqa</button>
<p>{tab}</p>
</div>
);
}Konsolda:
transition: 256
bosish: 2Ekranda:
TaomlarAloqa
aloqa2 — SyncLane: bosish kabi diskret hodisadagi yangilanish, eng shoshilinch. 256 — birinchi transition yo'lagi. React 19.3.0 kodida (react-dom) yana bir nechtasi bor. 8 — uzluksiz hodisa (scroll, sichqoncha harakati), 32 — sukut (masalan setTimeout ichidagi set…). Transition'lar uchun 256 dan boshlab 14 ta yo'lak, eng oxirida — Idle. Son kichik — ustuvorlik yuqori.
Bu bilim kundalik kod uchun shart emas — siz yo'lakni hech qachon qo'lda tanlamaysiz. Lekin "transition nega kutadi" ning javobi shu: 256 yo'lagidagi ish 2 kelganda chetga suriladi. Profiler va Performance panelidagi "Blocking" va "Transition" treklari ham shu yo'laklar guruhlari (Performance'ni o'lchash).
Tekshirib ko'ring: Nega Fiber daraxti massivlar emas,
child/sibling/returnko'rsatkichlari bilan qurilgan?
Javob
To'xtab, keyin davom etish uchun. Ko'rsatkichlar bilan "hozir qayerdaman" — bitta o'zgaruvchi (workInProgress). Undan keyingi qadam doim ma'lum: child, bo'lmasa sibling, bo'lmasa return. Rekursiv aylanishda esa holat JavaScript chaqiruvlar stekida — uni saqlab qo'yib, 5 ms dan keyin qaytib bo'lmaydi.
7. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| ro'yxatdagi input matni boshqa qatorga o'tdi | key={index}, boshiga qo'shish yoki saralash |
key={item.id} |
| har renderda fokus va state yo'qoladi | komponent boshqa komponent ichida e'lon qilingan | modul darajasiga chiqaring |
<div> ni <section> qilsam, state nolga tushdi |
o'sha joyda type o'zgardi |
o'ram turini saqlang yoki state'ni ko'taring |
key={Math.random()} — hammasi sekin, fokus yo'q |
har renderda yangi key — har qator qayta yaratiladi | barqaror id |
Each child in a list should have a unique "key" prop. |
map da key yo'q |
ma'lumotdagi id (Ro'yxatlar va key) |
__reactFiber$ ga tayangan kod yangilanishda buzildi |
ichki API | props, ref, context |
8. Mashqlar
Mashqlar kurs/mashqlar/18/17-reconciliation/ da. Har yechimni «Ishga tushir» bilan sinang.
1-mashq (oson): saqlanadimi?
Har o'zgarishda Counter ning state'i saqlanadimi?
<div><Counter /></div>→<div className="faol"><Counter /></div>:<div><Counter /></div>→<section><Counter /></section>:<Counter key="osh" />→<Counter key="manti" />:<Counter color="red" />→<Counter color="green" />:
Yechim
Birinchisida div o'sha — faqat atribut o'zgardi (props farqi). Ikkinchisida ota type i o'zgardi — butun ichi qayta yaratiladi. Uchinchisida key o'zgardi — React buni boshqa komponent deb biladi. To'rtinchisida — o'sha tur, o'sha joy, faqat props: state saqlanadi. Qoida: "turi va key o'sha bo'lsa — saqlanadi".
2-mashq (o'rta): saralashdagi tuzoq
Buyurtmalar ro'yxatida har qatorda izoh maydoni bor. Mehmon Osh izohiga "achchiq" deb yozdi, keyin «Narx bo'yicha» ni bosdi. Tartib o'zgardi — va izoh boshqa taomga ko'chib ketdi. Faqat bitta joyni o'zgartirib tuzating. Tekshiruvchi Osh maydoniga yozadi, «Narx bo'yicha» ni bosadi, keyin «Ko'rsat» har qatordagi taomni va uning maydonidagi matnni konsolga chiqaradi.
import { useState } from "react";
const ORDERS = [
{ id: 1, dish: "Osh", price: 35000 },
{ id: 2, dish: "Lag'mon", price: 28000 },
{ id: 3, dish: "Manti", price: 30000 },
];
function App() {
const [sorted, setSorted] = useState(false);
const list = sorted
? ORDERS.toSorted((a, b) => a.price - b.price)
: ORDERS;
function show() {
for (const li of document.querySelectorAll("li")) {
const note = li.querySelector("input").value;
console.log(li.dataset.dish, "→", note);
}
}
return (
<div>
<button onClick={() => setSorted(true)}>Narx bo'yicha</button>
<button onClick={show}>Ko'rsat</button>
<ul>
{list.map((o, i) => (
<li key={i} data-dish={o.dish}>
{o.dish} <input aria-label={`${o.dish} izohi`} />
</li>
))}
</ul>
</div>
);
}Konsolda:
Lag'mon → achchiq
Manti →
Osh → Yechim
import { useState } from "react";
const ORDERS = [
{ id: 1, dish: "Osh", price: 35000 },
{ id: 2, dish: "Lag'mon", price: 28000 },
{ id: 3, dish: "Manti", price: 30000 },
];
function App() {
const [sorted, setSorted] = useState(false);
const list = sorted
? ORDERS.toSorted((a, b) => a.price - b.price)
: ORDERS;
function show() {
for (const li of document.querySelectorAll("li")) {
const note = li.querySelector("input").value;
console.log(li.dataset.dish, "→", note);
}
}
return (
<div>
<button onClick={() => setSorted(true)}>Narx bo'yicha</button>
<button onClick={show}>Ko'rsat</button>
<ul>
{list.map((o) => (
<li key={o.id} data-dish={o.dish}>
{o.dish} <input aria-label={`${o.dish} izohi`} />
</li>
))}
</ul>
</div>
);
}Konsolda:
Lag'mon →
Manti →
Osh → achchiqIndeks-key bilan React birinchi <li> ni (ichidagi input bilan) saqladi va unga "Lag'mon" matnini yozdi. Input esa uncontrolled — uning qiymati React state'ida emas, DOM'da (Formalar II). Shuning uchun "achchiq" o'z joyida, endi Lag'mon yonida qoldi. key={o.id} bilan React <li> larni ko'chiradi: "achchiq" li input Osh bilan birga pastga tushdi. toSorted — asl massivni o'zgartirmaydigan saralash (Nusxa bilan o'zgartirish: toSorted).
3-mashq (qiyin): bir xil tur, boshqa key
Taom kartasi (DishCard) ichida "Sevimli" belgisi (state) bor. Mehmon taomni almashtirsa (Osh → Manti), belgi yangi taomga ham o'tib qolyapti. DishCard ichini o'zgartirmasdan, App da bitta atribut bilan tuzating: taom almashsa, karta noldan boshlansin. Kutilgan konsol: Oshni sevimli qilib, Mantiga o'tilgandan keyin Manti: sevimli emas.
import { useState } from "react";
function DishCard({ dish }) {
const [liked, setLiked] = useState(false);
console.log(`${dish}: ${liked ? "sevimli" : "sevimli emas"}`);
return (
<div>
<h2>{dish}</h2>
<button onClick={() => setLiked(true)}>Sevimli</button>
</div>
);
}
function App() {
const [dish, setDish] = useState("Osh");
return (
<div>
<button onClick={() => setDish("Manti")}>Manti</button>
<DishCard dish={dish} />
</div>
);
}Yechim
import { useState } from "react";
function DishCard({ dish }) {
const [liked, setLiked] = useState(false);
console.log(`${dish}: ${liked ? "sevimli" : "sevimli emas"}`);
return (
<div>
<h2>{dish}</h2>
<button onClick={() => setLiked(true)}>Sevimli</button>
</div>
);
}
function App() {
const [dish, setDish] = useState("Osh");
return (
<div>
<button onClick={() => setDish("Manti")}>Manti</button>
<DishCard key={dish} dish={dish} />
</div>
);
}Konsolda:
Osh: sevimli emas
Osh: sevimli
Manti: sevimli emaskey={dish} — "har taom — alohida karta". Osh dan Manti ga o'tganda key o'zgardi, React eski DishCard Fiber'ini (state bilan) o'chirib, yangisini yaratdi. Bu props o'zgarganda state'ni effekt bilan "tozalash" dan yaxshiroq: bitta render, oraliq noto'g'ri holat yo'q (Sizga effekt kerak emas).
4-mashq: vazifalar da key'ni buzib ko'ring
vazifalar ning vaqtinchalik nusxasida ToliqRoyxat dagi key={vazifa.id} ni key={i} ga almashtiring (map((vazifa, i) => …)). npm run dev qiling va uchta vazifa yarating. Ikkinchisini tahrirlashni boshlang (matnni o'zgartiring, saqlamang), keyin boshqa tabda birinchisini bajarilgan qiling. Nima bo'ladi? Keyin o'zgarishni qaytaring — commit qilmang.
Yechim
Birinchi vazifa bajarilganda u pastga tushadi — tartib o'zgaradi. Indeks-key bilan tahrir qatorining DOM'i o'sha o'rinda qoladi — TahrirQatori ning state'i va maydondagi yozilgan matn bilan birga. Lekin o'sha o'ringa endi boshqa vazifa keladi: yozilgan matn boshqa vazifaga "o'tadi" yoki tahrirda belgisi bilan mos kelmay qoladi. key={vazifa.id} bilan React qatorni ko'chiradi — tahrir o'sha vazifada. Shuning uchun kanon izohi "fokus va yozilgan matn saqlanadi" deydi.
9. Real ishda
- Ro'yxatlar: jadval, chat, kanban — hamma joyda
key= ma'lumotdagi id. Server id bermasa (yangi, hali saqlanmagan element) —crypto.randomUUID()bilan bir marta, element yaratilganda (Mini-ilova). - Forma reset: "boshqa foydalanuvchi profilini ochdi — forma tozalansin" —
key={userId}. - Kutubxonalar: React DevTools, react-scan va testing kutubxonalari Fiber daraxtini o'qiydi; siz ularni ishlatasiz, Fiber'ni emas.
- Intervyu savollari: "Virtual DOM nima va u tezmi?", "Reconciliation qanday ishlaydi?", "Nega
key={index}yomon?", "Fiber nima uchun kerak bo'ldi?", "React render'ni qanday to'xtatadi?".
Xulosa
- Render natijasi — element obyektlari (
type,props,key); "Virtual DOM" — shu daraxt, u DOM'dan tez emas, deklarativ yozishni ta'minlaydi. - Diffing ikki qoida bilan O(n): turi boshqa — butun daraxt qayta yaratiladi (state yo'qoladi); bolalar
keybo'yicha juftlanadi. - O'lchov: boshiga qo'shishda
key={i}— 4 DOM o'zgarishi,key={nomi}— 1; indeks-key holatni boshqa ma'lumotga "yopishtiradi". - Komponent ichida e'lon qilingan komponent — har renderda yangi tur, demak har renderda qayta yaratiladi.
- Fiber — doimiy ish birligi (
child/sibling/return,alternate); work loop uni sikl bilan ishlaydi vashouldYieldda to'xtaydi; lanes — ustuvorlik (bosish2, transition256).
Keyingi dars: Qayta render sabablari va miflar — komponent aynan nima uchun qayta chiziladi va "props o'zgardi" nega sabab emas.
Manbalar
- React: "Preserving and Resetting State" — react.dev/learn/preserving-and-resetting-state
- React (legacy): "Reconciliation" — legacy.reactjs.org/docs/reconciliation.html
- React Fiber Architecture (Andrew Clark, 2016) — github.com/acdlite/react-fiber-architecture
react-dom19.3.0 manba kodi:getHighestPriorityLanes,DiscreteEventPriority = 2,DefaultEventPriority = 32— npm paketi
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!