IlmHamroh
JavaScript Full-stack/18-qism. React chuqur18/42-dars17 daqiqa
Mundarija (29)

Qayta render sabablari va miflar: komponent aynan nega chiziladi

Qisqacha: Komponent faqat uch sababdan qayta chiziladi: (1) o'z state'i o'zgardi (useState, useReducer, tashqi do'kon), (2) otasi qayta chizildi, (3) o'qiyotgan context qiymati o'zgardi. "Props o'zgardi" — alohida sabab emas: props faqat ota chizilganda keladi, ota chizilsa esa bola props o'zgarmasa ham chiziladi. Bir xil qiymatni set qilsangiz React chizishni to'xtatadi (Object.is), lekin yangi obyekt — doim "yangi". vazifalar dagi bitta katakka 18 056 render — shu uch qoidaning zanjiri: biz uni qatorma-qator ochamiz.

Bu darsda

  • Qayta renderning uchta sababini konsol loglari bilan ajrata olasiz.
  • "Props o'zgardi", "butun ilova chiziladi", "setState doim chizadi" kabi miflarni o'lchov bilan rad etasiz.
  • Bir xil qiymatda React qanday to'xtashini (bailout) va uning g'alati istisnosini bilasiz.
  • Render sonini to'g'ri o'lchash usullarini tanlaysiz.
  • vazifalar dagi 18 056 render qayerdan kelganini tushuntira olasiz.

Oldin bilishingiz kerak: Render va commit, Reconciliation, Virtual DOM va Fiber, Context II: performance va chegaralar, React DevTools bilan ishlash.

1. Nega bu kerak?

Render va commit darsidagi jadvalda bitta raqam ko'zga tashlandi: 1 000 vazifali ro'yxatda bitta katakni bosish — 18 056 komponent render. Bitta vazifa o'zgardi, 999 tasi o'sha. Nega React ularning hammasini qayta chaqirdi?

Sardor birinchi xayolini aytdi: "Props o'zgargandir-da". Jasur aka so'radi: "Qaysi props? 999 ta qatorning matni ham, holati ham o'sha-ku". Bu savolga javob topish uchun avval qoidani aniq bilish kerak: React komponentni qachon chaqiradi. Taxmin bilan optimallashtirsangiz, memo ni noto'g'ri joyga qo'yasiz va hech narsa tezlashmaydi.

Hayotiy o'xshatish: «Bahor» oshxonasida taom uch holatda qayta tayyorlanadi. Mehmon buyurtmasini o'zgartirdi (o'z state'i). Bosh oshpaz butun stolning buyurtmasini qayta boshladi (ota). Yoki oshxonaning umumiy qoidasi o'zgardi — "bugundan hamma taom achchiqsiz" (context). Boshqa sabab yo'q. "Taomning retsepti o'zgardi" — bu ham faqat shu uchtasidan biri orqali sodir bo'ladi.

2. Uch sabab

2.1 Bitta misolda uchalasi

«Bahor» sahifasida uch qism bor. Menu — narx state'i bilan, Footer — memo bilan o'ralgan, uning ichida esa tema context'ini o'qiydigan ThemeLabel. memo ni useTransition darsida bir gap bilan ko'rgansiz: props o'sha bo'lsa, komponent qayta chizilmaydi. Har komponent chizilganda ismini yozadi:

jsx
import { createContext, memo, use, useState } from "react";

const ThemeContext = createContext("yorug'");

function Logo() {
  console.log("· Logo");
  return <h1>«Bahor»</h1>;
}

function Price({ value }) {
  console.log("· Price");
  return <p>Osh: {value} so'm</p>;
}

function Menu() {
  const [price, setPrice] = useState(35000);
  console.log("Menu");
  return (
    <section>
      <Logo />
      <Price value={price} />
      <button onClick={() => setPrice(price + 1000)}>Narx +</button>
    </section>
  );
}

function ThemeLabel() {
  const theme = use(ThemeContext);
  console.log("· ThemeLabel");
  return <p>Tema: {theme}</p>;
}

const Footer = memo(function Footer() {
  console.log("Footer");
  return <ThemeLabel />;
});

function App() {
  const [theme, setTheme] = useState("yorug'");
  console.log("App");
  return (
    <ThemeContext value={theme}>
      <Menu />
      <Footer />
      <button onClick={() => setTheme("qorong'i")}>Tema</button>
    </ThemeContext>
  );
}

Konsolni uch bo'lakka bo'lib o'qing: birinchi chizish, «Narx +» va «Tema». Har bo'lakda kim chizilgani — keyingi jadvallarda.

Konsolda:

text
App
Menu
· Logo
· Price
Footer
· ThemeLabel
Menu
· Logo
· Price
App
Menu
· Logo
· Price
· ThemeLabel

Ekranda:

text
«Bahor»
Osh: 36000 so'm
Narx +
Tema: qorong'i
Tema

Birinchi olti qator — birinchi chizish. Keyin ikki bosish.

2.2 «Narx +»: o'z state'i va ota

Kim Chizildimi Sabab
App yo'q uning state'i o'zgarmadi
Menu ha o'z state'i (price)
Logo ha otasi (Menu) chizildi
Price ha otasi chizildi
Footer, ThemeLabel yo'q ular Menu ning ostida emas

Ikki narsaga e'tibor bering. Logo ga hech qanday props berilmagan — u baribir chizildi. Sababi faqat bitta: Menu qayta chizildi, Menu esa o'z JSX'ida <Logo /> ni qaytaradi. React sukut bo'yicha otasi chizilgan har bolani chizadi.

Ikkinchisi: state Menu da yashaydi va render o'sha joydan pastga ketadi. Yuqoridagi App va yonidagi Footer tegilmadi. "Bitta state o'zgarsa, butun ilova qayta chiziladi" — mif: faqat state egasi va uning ostidagi daraxt.

2.3 «Tema»: ota va context

Kim Chizildimi Sabab
App ha o'z state'i (theme)
Menu, Logo, Price ha otasi chizildi (zanjir)
Footer yo'q memo, props yo'q — o'sha
ThemeLabel ha context (ThemeContext) o'zgardi

Eng qiziq qator — oxirgisi. Footer chizilmadi, demak "ota chizildi" sababi ThemeLabel ga yetib kelmadi. Lekin ThemeLabel baribir chizildi: u use(ThemeContext) bilan context o'qiydi (use()), context qiymati esa "yorug'" dan "qorong'i" ga o'zgardi. React context'ni o'qiydigan har komponentni topib, uni o'zi chizadi — oradagi memo larni aylanib o'tib.

flowchart TD
  A{"Komponent chiziladimi?"} --> B["O'z state'i o'zgardi"]
  A --> C["Otasi chizildi"]
  A --> D["O'qiyotgan context o'zgardi"]
  C --> E{"memo bormi, props o'shami?"}
  E -->|"ha"| F["Chizilmaydi"]
  E -->|"yo'q"| G["Chiziladi"]
  B --> G
  D --> G

Diagrammaga qarang: memo faqat bitta sababni — "ota chizildi" ni — to'xtata oladi. O'z state'i va context — memo dan o'tadi.

Tekshirib ko'ring: Agar Footer dan memo olib tashlansa, «Tema» bosilganda konsolda nima o'zgaradi?

Javob

Footer qatori qo'shiladi (App chizildi → Footer uning bolasi). ThemeLabel ham chiziladi — endi ikki sabab bilan: otasi chizildi va context o'zgardi. React uni baribir bir marta chizadi. Ya'ni bu misolda memo faqat bitta keraksiz renderni (Footer) tejadi.

3. "Props o'zgardi" mifi

3.1 Props o'z-o'zidan o'zgarmaydi

Props — ota renderida yasalgan obyekt. Bola uni "kuzatmaydi", React ham "props o'zgardimi" deb vaqti-vaqti bilan tekshirmaydi. Props'ning yangi qiymati faqat ota chizilganda keladi. Demak, "props o'zgardi, shuning uchun chizildi" aslida "ota chizildi, shuning uchun chizildi (props ham boshqa bo'lib qoldi)".

Teskarisi ham to'g'ri: props o'sha bo'lsa ham, ota chizilsa — bola chiziladi (Logo misoli). Props'ga qaraydigan yagona narsa — memo: u ota renderida bolaning yangi props'ini eskisi bilan solishtiradi va o'sha bo'lsa, renderni to'xtatadi (memo, useMemo, useCallback).

3.2 React faqat set ni eshitadi

Mifning ikkinchi yuzi: "ma'lumot o'zgarsa, React o'zi chizadi". Yo'q — React faqat set…, dispatch va context o'zgarishini biladi:

jsx
let guests = 0; // ❌ modul o'zgaruvchisi — React bilmaydi

function App() {
  console.log("App chizildi, mehmonlar:", guests);
  function handleClick() {
    guests += 1;
    console.log("guests =", guests);
  }
  return (
    <div>
      <p>Bugun mehmonlar: {guests}</p>
      <button onClick={handleClick}>Mehmon keldi</button>
    </div>
  );
}

Konsolda:

text
App chizildi, mehmonlar: 0
guests = 1
guests = 2

Ekranda:

text
Bugun mehmonlar: 0
Mehmon keldi

O'zgaruvchi 2 bo'ldi, ekran 0 da qoldi. React uchun hech narsa sodir bo'lmadi — trigger yo'q (Render va commit). Xuddi shu narsa props obyektini, state ichidagi massivni to'g'ridan-to'g'ri o'zgartirganda ham bo'ladi (State III: immutable yangilash).

4. "setState doim chizadi" mifi

4.1 Bir xil qiymat — bailout

set ga hozirgi qiymatning o'zini bersangiz, React chizishni boshlamaydi. Solishtirish — Object.is (Tenglik va Object.is). Bu bailout (chiqib ketish) deyiladi:

jsx
import { useState } from "react";

function App() {
  const [dish, setDish] = useState("Osh");
  console.log("App chizildi:", dish);
  return <button onClick={() => setDish("Osh")}>Osh</button>;
}

Konsolda:

text
App chizildi: Osh

Uch bosish — birorta ham render yo'q. React "Osh" ni "Osh" bilan solishtirdi, Object.is — true, ish shu yerda tugadi.

4.2 G'alati istisno: "yana bir marta"

Endi boshqa tartib: avval qiymat o'zgaradi, keyin o'sha yangi qiymat yana ikki marta beriladi:

jsx
import { useState } from "react";

function App() {
  const [dish, setDish] = useState("Osh");
  console.log("App chizildi:", dish);
  return <button onClick={() => setDish("Manti")}>Manti</button>;
}

Konsolda:

text
App chizildi: Osh
App chizildi: Manti
App chizildi: Manti

Ikkinchi bosish ("Manti" → "Manti") bitta ortiqcha render berdi, uchinchisi — yo'q. Bu xato emas, hujjatlangan xulq. React hujjati (useState, "Caveats") shunday deydi: qiymat o'sha bo'lsa, React komponentni va uning bolalarini qayta chizmaydi, lekin ba'zan bolalarga o'tmasdan oldin komponentning o'zini chaqirishi mumkin.

Sababi — Fiber dagi ikki daraxt. Object.is bilan oldindan tekshirish uchun React Fiber'ning ikkala nusxasi (current va alternate) "kutilayotgan ish yo'q" holatida bo'lishini talab qiladi. Birinchi o'zgarishdan keyin nusxalardan birida hali eski belgi qoladi. Shuning uchun React bir marta komponentni chaqiradi, natija o'sha ekanini ko'radi va commit'siz to'xtaydi. Amaliy xulosa bitta: bu render arzon (bolalar chizilmaydi) va unga tayanmang — console.log sonini "aniq hisob" deb ishlatmang.

4.3 Yangi obyekt — doim yangi

Obyekt bilan esa bailout deyarli ishlamaydi:

jsx
import { useState } from "react";

function App() {
  const [order, setOrder] = useState({ dish: "Osh", qty: 1 });
  console.log("App chizildi:", order.dish, order.qty);
  return (
    <button onClick={() => setOrder({ ...order })}>Saqla</button>
  );
}

Konsolda:

text
App chizildi: Osh 1
App chizildi: Osh 1
App chizildi: Osh 1

{ ...order } — mazmuni o'sha, lekin yangi obyekt. Object.is(eski, yangi) — false. React mazmunni chuqur solishtirmaydi: bu qimmat bo'lardi va har doim ham to'g'ri emas. Bu mif emas, qoida: React havola (reference) bo'yicha solishtiradi. Qoida memo, useEffect massivi va context qiymatida ham bir xil — keyingi darslarning asosiy mavzusi.

Tekshirib ko'ring: setItems(items.filter(() => true)) — mazmun o'sha. Qayta render bo'ladimi?

Javob

Bo'ladi. filter doim yangi massiv qaytaradi, hatto hech narsa olib tashlanmasa ham. Object.is havolani solishtiradi — false. Chizishni oldini olish kerak bo'lsa, o'zgarish yo'qligini o'zingiz tekshiring va eski massivni qaytaring: setItems((eski) => (yangi.length === eski.length ? eski : yangi)).

5. Context: hamma iste'molchi

Context o'zgarganda React uni o'qiydigan har komponentni chizadi. "O'zgardi" — yana Object.is bo'yicha. Context qiymati obyekt bo'lsa va u har renderda yangidan yasalsa — har renderda "o'zgardi":

jsx
import { createContext, memo, use, useState } from "react";

const ThemeContext = createContext(null);

const ThemeButton = memo(function ThemeButton() {
  const { setTheme } = use(ThemeContext);
  console.log("ThemeButton");
  return (
    <button onClick={() => setTheme("qorong'i")}>Qorong'i</button>
  );
});

function App() {
  const [theme, setTheme] = useState("yorug'");
  const [count, setCount] = useState(0);
  console.log("App", count, theme);
  return (
    <ThemeContext value={{ theme, setTheme }}>
      <ThemeButton />
      <button onClick={() => setCount(count + 1)}>Sanagich</button>
    </ThemeContext>
  );
}

Konsolda:

text
App 0 yorug'
ThemeButton
App 1 yorug'
ThemeButton
App 1 qorong'i
ThemeButton

«Sanagich» temaga umuman tegmaydi. ThemeButton — memo da va faqat setTheme ni ishlatadi, u esa doim o'sha funksiya. Lekin baribir chizildi: { theme, setTheme } — har App renderida yangi obyekt, demak context "o'zgardi". Bu Context II darsidagi chegara. Davosi — qiymatni barqaror qilish (useMemo, 3-mashqda) yoki context'ni ikkiga bo'lish.

6. "Qayta render — yomon" mifi

Oxirgi va eng zararli mif. Yuqoridagi misollarda ortiqcha renderlarni topdik — demak, hammasini yo'qotish kerakmi? Yo'q. Render — oddiy JavaScript funksiyasi chaqiruvi. Logo kabi komponent mikrosekundlarda chiziladi, DOM'ga esa natija o'sha bo'lsa umuman tegilmaydi (Render va commit).

vazifalar dagi raqamlar buni yaxshi ko'rsatadi. E'lon o'chganda 17 ta komponent qayta chiziladi — ularning ko'pi "keraksiz". Lekin bu foydalanuvchi sezadigan hech narsa emas: bitta kadrning kichik bo'lagi. Katak bosilganda esa 18 056 render — 1 000 vazifada 48 ms, 10 000 da 264 ms. Farq — soni va narxi, "render bor-yo'qligi" emas.

Shuning uchun tartib doim bir xil:

  1. Sekinlikni seziladigan joyda o'lchang (INP, Profiler).
  2. Qaysi komponent ko'p yoki qimmat chizilayotganini toping.
  3. Uchta sababdan qaysi biri ekanini aniqlang.
  4. Faqat shundan keyin davolang — avval kompozitsiya, keyin memo.

Har komponentni oldindan memo ga o'rash — o'zi ham narx: har renderda props solishtiriladi, kod murakkablashadi, yangi obyekt berilsa esa baribir ishlamaydi. Bu "erta optimallashtirish" ning klassik ko'rinishi (memo, useMemo, useCallback).

7. Render sonini qanday o'lchash

Usul Nimani sanaydi E'tibor
console.log render tanasida har chaqiruvni StrictMode'da ikki marta, tashlangan renderlar ham
useRef hisoblagich useEffect da commit'larni Render va commit dagi 3-mashq
DevTools "Highlight updates" ko'z bilan, tez aniq son yo'q
DevTools Profiler, "Why did this render?" har commit, har komponent va sabab yozib olish kerak
<Profiler onRender> daraxt qismi, vaqt bilan Performance'ni o'lchash
DevTools hook'i (kanon skripti) butun ilova, avtomatik o'lchov vositalari uchun

Profiler'da sozlamalardan "Record why each component rendered while profiling" yoqilsa, har komponent uchun "Why did this render?" (nega chizildi?) bo'limi to'ladi (React ilovani debug qilish). Uning javoblari aynan shu darsdagi uchta sabab:

DevTools yozuvi Sabab
"The parent component rendered." ota chizildi
"• Hook 1 changed" yoki "• Hook changed:" + hooklar ro'yxati o'z state'i (1-hook)
"• Context changed" context
"• Props changed:" + props nomi (masalan vazifalar) ota chizildi, props boshqa

Oxirgi qator alohida sabab emas — bu ham "ota chizildi" ning bir ko'rinishi. Matnlar React DevTools manbasidan (react-devtools-shared, WhatChanged.js, react-devtools-core 8.0.0) olindi.

8. Vazifalar qadami: 18 056 renderni ochamiz (o'lchov, commit yo'q)

8.1 Ssenariylar

Kanon o'lchovi (natijalar/18-renderlar-12.txt, holat 12, 1 000 vazifa) — har ssenariyda nechta komponent render:

vazifalar, 1 000 vazifa: bitta harakatga komponent renderlari
  • katak bosish5 commit18 056 render
  • yangi vazifa qo'shish5 commit18 068 render
  • qidiruv: bitta harf2 commit6 035 render
  • qidiruv: 9 harf10 commit237 render
  • e'lon o'chishi1 commit17 render
  • tema tanlovi1 commit2 render

Manba: O'lchandi: vazifalar holat 12, olchov/renderlar.mjs (React DevTools hook usuli), Chrome headless, 1 000 vazifa, soxta server, 2026-10-13

Grafikda ikki ustun tepada, qolganlari deyarli ko'rinmaydi. Har birini uchta sabab bilan tushuntiramiz.

8.2 Katak: 18 056 = zanjir

Bosish App dagi vazifalar state'ini o'zgartiradi (optimistik, keyin server tasdig'i — useOptimistic). Keyin zanjir:

  1. App — o'z state'i. vazifalar o'zgardi.
  2. VazifaRoyxati — ota. U memo da (#02), lekin props'i korinadigan — useMemo([vazifalar, …]) natijasi. vazifalar o'zgardi → yangi massiv → memo "props o'zgardi" deydi.
  3. 1 000 ta VazifaQatori — ota. Ular memo siz. VazifaRoyxati chizildi → hammasi chizildi.
  4. Har qator ichida 5 komponent — ota. OddiyQator, Qator, BelgilanganMatn va ikkita AmalTugmasi.

Bir qatorga 6 komponent × 1 000 qator × 3 commit = 18 000. Qolgan 56 — App, Aloqa, XatoChegarasi, StatistikaPaneli va boshqalar (faylda: AmalTugmasi×6000, VazifaQatori×3000, OddiyQator×3000, Qator×3000, BelgilanganMatn×3000). Nega 5 commit'dan 3 tasi? Faylda App×3 — qolgan ikki commit'da App umuman chizilmagan, ular ro'yxatdan tashqaridagi kichik yangilanishlar edi (StatistikaPaneli×4 shu yerdan).

Endi asosiy savol: VazifaQatori ga memo qo'ysak — yetadimi? Kanon QADAMLAR.md dagi tashxis: yo'q. korinadiganVazifalar() har chaqirilganda vazifalardan yangi Vazifa obyektlarini yasardi (Klasslar). O'zgarmagan 999 vazifa ham yangi obyekt bo'lib kelardi — memo(VazifaQatori) ning Object.is(eski.vazifa, yangi.vazifa) i doim false. «Yangi obyekt — doim yangi» bo'limidagi qoida aynan shu yerda.

8.3 Qolganlari

  • Qidiruv, bitta harf ("v") — 6 035. "v" ni hamma vazifa o'z ichiga oladi: har qatorda belgilangan matn (BelgilanganMatn) haqiqatan o'zgaradi. Bu keraksiz render emas — kerakli ish. Uni kamaytirish emas, to'xtatib bo'ladigan qilish kerak edi (useDeferredValue).
  • E'lon o'chishi — 17. App ning elon state'i o'zgardi → App va uning memo siz bolalari (Aloqa, forma, filtrlar, Statistika…). Ro'yxat tegilmadi: VazifaRoyxati ning props'i o'sha, memo ishladi.
  • Tema — 2. TemaProvider (o'z state'i) va TemaTanlov (context). App umuman chizilmadi — nega, keyingi darsda (Kompozitsiya orqali optimallashtirish).

Vaqtda (natijalar/20-oldin.txt): katak — 1 000 vazifada mediana 48 ms, 10 000 da 264 ms. Tuzatish — memo, useMemo, useCallback darsida: asl obyektlar + memo(VazifaQatori) = 18 056 → 68.

9. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
props yo'q komponent ham chiziladi otasi chizildi kompozitsiya yoki memo
memo bor, baribir chiziladi props'da yangi obyekt/funksiya yoki context barqaror props, context'ni barqarorlash
o'zgaruvchi o'zgardi, ekran eski React faqat set ni biladi state yoki tashqi do'kon (useSyncExternalStore)
bir xil qiymatdan keyin bitta ortiqcha log hujjatlangan bailout istisnosi e'tibor bermang, natija commit'siz
setX({...x}) har safar chizadi yangi obyekt o'zgarmagan bo'lsa eskisini qaytaring
render sonini console.log bilan sanash noto'g'ri StrictMode, tashlangan renderlar Profiler yoki effektdagi hisob

10. Mashqlar

Mashqlar kurs/mashqlar/18/18-render-sabablari/ da. Har yechimni «Ishga tushir» bilan sinang.

1-mashq (oson): sababni toping

Har holatda komponent nega chizilgan?

  • Header hech qanday props olmaydi, App dagi soat har soniya yangilanadi:
  • CartBadge use(CartContext) qiladi, savatga taom qo'shildi:
  • Counter ichida setCount(count + 1):
  • Price ning value props'i o'zgardi:
Yechim

Ota, context, state — va to'rtinchisi ham ota. value faqat ota qayta chizilganda yangi qiymat bilan keladi; ota chizilmasa, props o'zgarishi mumkin emas. "Props o'zgardi" — oqibat, sabab emas.

2-mashq (o'rta): mehmonlar hisoblagichi

«Mehmonlar hisoblagichi» bo'limidagi kod ekranni yangilamaydi. Uni shunday tuzating: har bosishda ekran yangilansin va konsolga App chizildi, mehmonlar: N chiqsin. Ikki bosishdan keyin kutilgan natija:

Konsolda:

text
App chizildi, mehmonlar: 0
App chizildi, mehmonlar: 1
App chizildi, mehmonlar: 2
Yechim
jsx
import { useState } from "react";

function App() {
  const [guests, setGuests] = useState(0);
  console.log("App chizildi, mehmonlar:", guests);
  return (
    <div>
      <p>Bugun mehmonlar: {guests}</p>
      <button onClick={() => setGuests(guests + 1)}>
        Mehmon keldi
      </button>
    </div>
  );
}

Ekranda:

text
Bugun mehmonlar: 2
Mehmon keldi

Modul o'zgaruvchisi o'rniga state: setGuests — trigger, React App ni yangi qiymat bilan chizadi. Modul o'zgaruvchisining yana bir yashirin xavfi bor: u komponentning hamma nusxalari uchun bitta. Sahifada ikkita hisoblagich bo'lsa, ular bir-birining sonini o'zgartirardi.

3-mashq (qiyin): context qiymatini barqarorlash

«Context: hamma iste'molchi» bo'limidagi misolni tuzating: «Sanagich» bosilganda ThemeButton chizilmasin, «Qorong'i» bosilganda esa chizilsin (u yerda theme haqiqatan o'zgaradi). Faqat App ni o'zgartiring. Ishora: context qiymati — obyekt; uni faqat theme o'zgarganda yangidan yasang — useMemo (useDeferredValue darsida bir gap bilan ko'rgansiz: massivdagi qiymatlar o'zgarmasa, o'sha natijani qaytaradi).

Yechim
jsx
import { createContext, memo, use, useMemo, useState } from "react";

const ThemeContext = createContext(null);

const ThemeButton = memo(function ThemeButton() {
  const { setTheme } = use(ThemeContext);
  console.log("ThemeButton");
  return (
    <button onClick={() => setTheme("qorong'i")}>Qorong'i</button>
  );
});

function App() {
  const [theme, setTheme] = useState("yorug'");
  const [count, setCount] = useState(0);
  const value = useMemo(() => ({ theme, setTheme }), [theme]);
  console.log("App", count, theme);
  return (
    <ThemeContext value={value}>
      <ThemeButton />
      <button onClick={() => setCount(count + 1)}>Sanagich</button>
    </ThemeContext>
  );
}

Konsolda:

text
App 0 yorug'
ThemeButton
App 1 yorug'
App 1 qorong'i
ThemeButton

«Sanagich» dan keyin ThemeButton yo'q: value — o'sha obyekt (theme o'zgarmadi), context "o'zgarmadi", memo esa "ota chizildi" ni to'xtatdi. «Qorong'i» da theme o'zgardi — yangi obyekt, ThemeButton chizildi. setTheme ni massivga qo'shish shart emas: React set funksiyasi doim o'sha (State I). Ikkala himoya ham kerak edi: memo siz ThemeButton «Sanagich» da otasi tufayli chizilardi.

4-mashq: "Why did this render?"

vazifalar holat 12 ni 1 000 vazifa bilan oching. React DevTools → Profiler, sozlamalarda "Record why each component rendered while profiling" ni yoqing. Yozib olishni boshlang, bitta katakni bosing, to'xtating. Uchta commit'ni tanlab, VazifaRoyxati va bitta VazifaQatori uchun "Why did this render?" ni o'qing. Javoblar «Katak: 18 056 = zanjir» bo'limidagi qaysi qadamlarga mos?

Yechim

VazifaRoyxati — "• Props changed:" va yonida vazifalar (2-qadam: yangi massiv). VazifaQatori — "The parent component rendered." (3-qadam). App da — "• Hook … changed" (1-qadam: state yoki optimistik qatlam). Flamegraph'da 1 000 ta qator sariq-yashil chiziqlar bilan to'lib ketadi — hammasi ishlagan. Profiler bilan ishlash to'liq — Performance'ni o'lchash darsida.

11. Real ishda

  • Kod ko'rigi (code review): "bu memo nimaga kerak?" savoliga javob — qaysi sababni to'xtatyapti. Agar komponent o'z state'i yoki context tufayli chizilsa, memo foydasiz.
  • Global state kutubxonalari: Zustand, Redux kabi kutubxonalar context'ning "hamma iste'molchi" muammosini selektorlar bilan hal qiladi — faqat kerakli bo'lak o'zgarsa chizadi (keyingi qismlarda).
  • Profiling madaniyati: katta jamoalarda "sekin" degan PR'ga Profiler skrinshoti va "Why did this render?" sabablari qo'shiladi.
  • Intervyu savollari: "Komponent qachon qayta render bo'ladi?", "Props o'zgarishi renderga sababmi?", "Nega setState bilan bir xil obyekt bersam ham chiziladi?", "Context nega hamma iste'molchini chizadi?".

Xulosa

  • Uch sabab: o'z state'i, ota render, o'qiyotgan context. Boshqa sabab yo'q.
  • "Props o'zgardi" — ota renderining oqibati; ota chizilsa, bola props'i o'sha bo'lsa ham chiziladi (memo siz).
  • Render state egasidan pastga ketadi — butun ilova emas.
  • Object.is: bir xil primitiv — bailout (ba'zan bitta arzon ortiqcha chaqiruv); yangi obyekt — doim "yangi".
  • Context o'zgarsa, har iste'molchi memo dan o'tib chiziladi; obyekt qiymat har renderda yangi bo'lsa — har renderda.
  • vazifalar: 18 056 = App state → yangi massiv → memo siz 1 000 qator × 6 × 3 commit; memo(VazifaQatori) yangi Vazifa obyektlari tufayli yetmasdi.

Keyingi dars: Kompozitsiya orqali optimallashtirish — memo dan oldin: state'ni pastga tushirish va children orqali uzatish renderlarni bepul kamaytiradi.

Manbalar

  • React: useState — Caveats (bir xil qiymat va bailout) — react.dev/reference/react/useState
  • React: "Render and Commit" — react.dev/learn/render-and-commit
  • React: memo — react.dev/reference/react/memo
  • Kanon: sinov/18/QADAMLAR.md (#16–#18), natijalar/18-renderlar-12.txt, natijalar/20-oldin.txt
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Qayta render sabablari va miflar: komponent aynan nega chiziladi — IlmHamroh