React rendering qanday ishlaydi — reconciliation, diffing

~20 daq · React · Performance

Muammodan boshlaylik

Uchta savol, javobini bilasizmi?

function App() {
  const [son, setSon] = useState(0);
  return (
    <>
      <button onClick={() => setSon(son + 1)}>{son}</button>
      <Sarlavha />           {/* props yo'q, hech narsa o'zgarmaydi */}
    </>
  );
}
  1. Tugma bosilganda <Sarlavha /> qayta render bo'ladimi? — Ha. Props bo'lmasa ham
  2. "Render" — DOM ni o'zgartirish degani mi? — Yo'q.
  3. Tab almashtirilganda nega forma qiymatlari ba'zan yo'qoladi, ba'zan qoladi?

Performance optimizatsiyasini boshlashdan oldin — React aslida nima qilishini tushunish kerak. Aks holda memo va useMemo ni "har ehtimolga qarshi" hamma joyga sochib, kodni murakkablashtirasiz va tezlikda deyarli hech narsa yutmaysiz.


Render nima

React da ekranni yangilash uch bosqichda bo'ladi:

1. TRIGGER          2. RENDER                        3. COMMIT
state o'zgardi  →   komponent funksiyalari      →   DOM ga faqat
(setState)          chaqiriladi, yangi JSX           farqlar yoziladi
                    daraxti yasaladi,
                    eskisi bilan solishtiriladi

1. Trigger — nima render ni boshlaydi

  • Komponentning o'z state i o'zgardi (setState, dispatch)
  • Ota-ona komponent render bo'ldi
  • Ishlatilayotgan Context qiymati o'zgardi
  • Tashqi store (Zustand, Redux) dagi tanlangan qiymat o'zgardi

[!INFO] Props o'zgarishi — alohida trigger emas. Props faqat ota-ona render bo'lganda o'zgaradi, va ota-ona render bo'lganda bola props o'zgarganmi yoki yo'qmi — baribir render bo'ladi. Bu — eng ko'p uchraydigan noto'g'ri tushuncha.

2. Render — komponentni chaqirish

"Render" — React ning komponent funksiyangizni chaqirishi:

function Sarlavha() {
  console.log('Sarlavha render');   // har renderda chiqadi
  return <h1>DevDocs</h1>;
}

Funksiya yangi JSX qaytaradi — bu Virtual DOM: oddiy JavaScript obyektlari daraxti (1-dars):

{ type: 'h1', props: { children: 'DevDocs' } }

Render — arzon: obyektlar yaratish va solishtirish. U haqiqiy DOM ga tegmaydi.

3. Commit — DOM ni yangilash

React yangi daraxtni eskisi bilan solishtiradi va faqat farqlarni DOM ga yozadi. Agar <h1>DevDocs</h1> o'zgarmagan bo'lsa — DOM ga hech narsa yozilmaydi.

Shuning uchun Sarlavha ning qayta render bo'lishi o'z-o'zidan muammo emas: funksiya chaqirildi, natija bir xil, DOM tegilmadi.


Reconciliation — solishtirish

Ikki daraxtni to'liq solishtirish algoritmik jihatdan juda qimmat (O(n³)). React ikki oddiy taxmin bilan uni chiziqli (O(n)) qiladi.

1-taxmin: turi o'zgarsa — hammasi yangidan

// oldin
<div>
  <Hisoblagich />
</div>

// keyin
<section>
  <Hisoblagich />
</section>

div → section: tur boshqa. React eski daraxtni butunlay yo'q qiladi (unmount) va yangisini noldan yaratadi. Hisoblagich ning state i yo'qoladi, garchi u o'zi o'zgarmagan bo'lsa ham.

Bir xil tur bo'lsa — DOM elementi saqlanadi, faqat o'zgargan atributlar yangilanadi:

<div className="eski" />   →   <div className="yangi" />     // faqat className o'zgaradi

2-taxmin: ro'yxatda key bilan

// oldin              // keyin (boshiga qo'shildi)
<li>Ali</li>          <li>Sardor</li>
<li>Vali</li>         <li>Ali</li>
                      <li>Vali</li>

key siz React tartib bo'yicha solishtiradi: 1-element "Ali" → "Sardor" (o'zgardi), 2-element "Vali" → "Ali" (o'zgardi), 3-element yangi. Hammasi qayta yoziladi.

key bilan — elementlar shaxsi bo'yicha:

<li key="s">Sardor</li>   // yangi — qo'shiladi
<li key="a">Ali</li>      // mavjud — tegilmaydi
<li key="v">Vali</li>     // mavjud — tegilmaydi

Shuning uchun key barqaror va noyob bo'lishi kerak, index emas (27-dars).


State qayerda yashaydi

Muhim tushuncha: state komponent funksiyasida emas, daraxtdagi joyida yashaydi. React uni "daraxtning shu pozitsiyasidagi shu turdagi komponent" bilan bog'laydi.

function App() {
  const [admin, setAdmin] = useState(false);
  return (
    <div>
      {admin ? <Forma rol="admin" /> : <Forma rol="mehmon" />}
    </div>
  );
}

Ikkala holatda ham div ning birinchi bolasi — Forma. Tur bir xil, joy bir xil → React uni bir xil komponent deb hisoblaydi → state saqlanadi. Admin/mehmon almashganda forma maydonlari tozalanmaydi.

Darsning boshidagi 3-savol javobi shu. Ataylab qayta yaratish kerak bo'lsa — key:

<Forma key={admin ? 'admin' : 'mehmon'} rol={admin ? 'admin' : 'mehmon'} />

key o'zgardi → React buni boshqa komponent deb hisoblaydi → eskisini unmount, yangisini mount → state toza.

// Profil sahifasi — foydalanuvchi o'zgarganda barcha state yangilansin
<ProfilTahrir key={userId} userId={userId} />
function Ota() {
  function Bola() { return <input />; }   // ❌ har renderda YANGI funksiya
  return <Bola />;
}

[!WARNING] Komponentni boshqa komponent ichida e'lon qilmang. Har renderda Bola — yangi funksiya, ya'ni yangi tur. React uni har safar unmount va mount qiladi: input fokusni va qiymatni yo'qotadi, effektlar qayta ishga tushadi. Komponentlarni har doim modul darajasida e'lon qiling.


Bola komponentlar qayta render bo'lishi

function App() {
  const [son, setSon] = useState(0);
  return (
    <>
      <button onClick={() => setSon(son + 1)}>{son}</button>
      <KattaRoyxat />                    {/* 1000 ta element */}
    </>
  );
}

Tugma bosilganda KattaRoyxat va uning 1000 ta bolasi qayta render bo'ladi. Odatda bu ham tez (millisekundlar). Lekin sekin bo'lsa — yechimlar:

1. State ni pastga tushirish

function App() {
  return (
    <>
      <Hisoblagich />                    {/* state shu yerda */}
      <KattaRoyxat />                    {/* endi render bo'lmaydi */}
    </>
  );
}

function Hisoblagich() {
  const [son, setSon] = useState(0);
  return <button onClick={() => setSon(son + 1)}>{son}</button>;
}

State faqat uni ishlatadigan komponentda. Eng oddiy va eng samarali optimizatsiya.

2. children orqali uzatish

function Scroll({ children }) {
  const [y, setY] = useState(0);
  // ... scroll ni kuzatish
  return <div data-y={y}>{children}</div>;
}

<Scroll>
  <KattaRoyxat />                        {/* App da yaratilgan — Scroll render bo'lsa ham qayta render yo'q */}
</Scroll>

children — ota-ona (App) yaratgan JSX. Scroll ning state i o'zgarganda children o'sha obyekt bo'lib qoladi va React uni qayta render qilmaydi.

3. React.memo

Yuqoridagilar ishlamasa — keyingi dars.


React Compiler

React jamoasi React Compiler ni chiqardi — build vaqtida komponentlarni avtomatik memoizatsiya qiluvchi vosita. U memo, useMemo, useCallback ni (68-dars, 15–16-darslar) ko'p hollarda keraksiz qiladi. Lekin u ham yuqoridagi qoidalarga asoslanadi: komponentlar sof bo'lishi, state o'zgarmas tarzda yangilanishi kerak. Rendering ni tushunish — kompilyator bilan ham, kompilyatorsiz ham zarur.


Nima uchun shunday?

Nima uchun React ota-ona render bo'lganda barcha bolalarni ham render qiladi — props o'zgarmaganini tekshirsa yaxshiroq emasmi?

Tekshirish ham bepul emas. Har bir komponent uchun barcha props larni oldingi qiymatlar bilan solishtirish — xotira (eski props larni saqlash) va vaqt talab qiladi. Ko'p komponentlar uchun esa qayta render qilish solishtirishdan arzonroq: kichik funksiya, bir necha JSX elementi. Bundan tashqari, props ning o'zi o'zgarmagan bo'lsa ham, komponent Context, tashqi store yoki joriy vaqtni o'qishi mumkin — "props bir xil" har doim ham "natija bir xil" degani emas. Shuning uchun React xavfsiz standartni tanlagan: hammasini render qil, DOM ni esa faqat zarur bo'lganda o'zgartir. Haqiqatan sekin joylarda esa sizga memo bilan "bu yerda solishtir" deyish imkoniyatini beradi.


Xulosa

Eslab qoling:

  • Yangilanish: trigger → render (funksiyani chaqirish, Virtual DOM) → commit (DOM ga faqat farqlar)
  • Render ≠ DOM o'zgarishi; render arzon, keraksiz DOM o'zgarishlari esa qimmat
  • Trigger: o'z state i, ota-ona render i, Context, tashqi store; props o'zgarishi alohida trigger emas
  • Ota-ona render bo'lsa — bolalar ham, props o'zgarmagan bo'lsa ham
  • Reconciliation: tur o'zgarsa — butunlay qayta yaratish; ro'yxatlarda key
  • State daraxtdagi joyda yashaydi; key ni o'zgartirish — state ni tozalash
  • Komponentni komponent ichida e'lon qilmang
  • Birinchi optimizatsiyalar: state ni pastga tushirish, children orqali uzatish

Keyingi dars

68-dars: React.memo — component ni keshlash, qachon kerak