Mundarija (35)
- Bu darsda
- 1. Nega bu kerak?
- 2. Havola tengligi
- 2.1 Primitiv va obyekt
- 2.2 Render — yangi obyektlar zavodi
- 3. memo
- 3.1 Asosiy ishlashi
- 3.2 Uch tuzatish
- 3.3 Custom compare — ehtiyot bo'ling
- 4. useMemo
- 4.1 Qimmat hisobni saqlash
- 4.2 Barqaror havola
- 4.3 Kafolat emas — optimallashtirish
- 5. useCallback
- 5.1 Funksiya uchun useMemo
- 5.2 Bo'sh massiv va updater
- 6. Qachon kerak emas
- 6.1 Erta optimallashtirish
- 6.2 Qachon kerak
- 7. Vazifalar qadami: qator memo si (holat 20)
- 7.1 Ikki sabab, ikki tuzatish
- 7.2 holat.ts: o'sha obyektlar
- 7.3 VazifaQatori: memo
- 7.4 App.tsx: hosilaviy qiymatlar useMemo da
- 7.5 O'lchov
- 7.6 Tekshiruvlar va commit
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): teng yoki yo'q?
- 2-mashq (o'rta): rangli qator
- 3-mashq (qiyin): savat va barqaror ishlovchi
- 4-mashq: vazifalar qadami
- 10. Real ishda
- Xulosa
- Manbalar
memo, useMemo, useCallback: qachon va qanday
Qisqacha:
memo(Component)— props avvalgisi bilan bir xil bo'lsa, komponentni qayta chizmaydi (har props alohida,Object.isbilan). HookuseMemo(() => hisob, [a, b])—avabo'zgarmaguncha o'sha natijani (o'sha havolani) qaytaradi.useCallback(fn, [a])— o'sha funksiyani. Uchalasi bitta muammoga xizmat qiladi: JavaScript'da{} !== {}, shuning uchun har renderda yasalgan obyekt va funksiyamemoni buzadi. Ular faqat o'lchangan muammoda kerak.vazifalarda bitta katak 1 000 vazifada 18 056 komponent render berardi. Ro'yxat o'sha obyektlarni qaytaradigan bo'ldi va qatormemoga o'raldi — 68 render, 48 → 32 ms.
Bu darsda
- Havola tengligi (referential equality) nega
memoning asosiy dushmani ekanini ko'rasiz. memoni yozasiz va uni buzadigan uchta props turini taniysiz.- Custom compare funksiyasi nega xavfli ekanini eskirgan closure misolida ko'rasiz.
useMemoni qimmat hisob va barqaror havola uchun,useCallbackni funksiya props uchun ishlatasiz.vazifalarda qatormemosini kiritib, 18 056 → 68 render natijasini o'lchaysiz va commit qilasiz.
Oldin bilishingiz kerak: Kompozitsiya orqali optimallashtirish, Qayta render sabablari va miflar, useEffect II: dependency massivi, Tenglik va Object.is.
1. Nega bu kerak?
Kompozitsiya darsining oxirida bitta holat qoldi: ro'yxat. vazifalar dagi 1 000 qator bitta massivdan chiziladi. Bitta vazifa o'zgarsa massiv yangi bo'ladi, ro'yxat qayta chiziladi va har qator — "ota chizildi" sababi bilan — u ham. 999 ta o'zgarmagan qatorni ro'yxatdan tashqariga chiqarib bo'lmaydi: ular ro'yxatning o'zi.
Bu yerda kompozitsiya ojiz, lekin memo aynan shu uchun yaratilgan: "props o'sha bo'lsa — chizma". Uni useTransition va useDeferredValue darslarida bir gap bilan ishlatgan edik va to'liq tushuntirishni va'da qilgan edik. Tashxis esa Qayta render sabablari darsida qo'yildi: memo(VazifaQatori) qo'shilsa ham yetmasdi, chunki har renderda yangi Vazifa obyektlari yasalardi. Bugun ikkalasini tuzatamiz.
Hayotiy o'xshatish: Jasur aka har kuni ertalab menyu taxtasini qayta yozadi. Aqlli yondashuv — kechagi taxtaga qarash: "taom va narx o'sha bo'lsa — o'chirmayman". Lekin bitta shart bor: solishtirish "narx yozilgan qog'oz o'sha qog'ozmi?" bo'yicha ketadi, "raqam o'shami?" bo'yicha emas. Har kuni yangi qog'ozga o'sha raqamni yozsangiz — u baribir "yangi" hisoblanadi. memo ning qoidasi ham shu.
2. Havola tengligi
2.1 Primitiv va obyekt
memo, useMemo, useEffect massivi va context — hammasi Object.is bilan solishtiradi (Tenglik va Object.is):
console.log(Object.is(35000, 35000)); // true
console.log(Object.is("Osh", "Osh")); // true
console.log(Object.is({ dish: "Osh" }, { dish: "Osh" })); // false
console.log(Object.is([1, 2], [1, 2])); // false
const order = () => "Osh";
console.log(Object.is(order, order)); // true
console.log(Object.is(() => "Osh", () => "Osh")); // falseSon va satr qiymat bo'yicha solishtiriladi. Obyekt, massiv va funksiya esa havola bo'yicha: "bu xotiradagi o'sha bitta obyektmi?" (Havola semantikasi). Mazmuni bir xil ikki obyekt — ikki xil obyekt.
2.2 Render — yangi obyektlar zavodi
Komponent tanasidagi har {…}, […] va () => … — har renderda yangi. Bu JavaScript qoidasi, React'niki emas: funksiya har chaqirilganda ichidagi literallar qaytadan yasaladi.
function App() {
const style = { color: "red" }; // har renderda yangi obyekt
const items = menu.filter(isHot); // har renderda yangi massiv
const onPick = () => setDish("Osh"); // har renderda yangi funksiya
…
}Odatda bu zararsiz va arzon. Muammo faqat shu qiymat solishtiriladigan joyga tushganda boshlanadi: memo li bolaning props'i, effekt massivi, context qiymati.
3. memo
3.1 Asosiy ishlashi
memo — komponentni o'raydigan funksiya: ota chizilganda React bolaning yangi props'ini eskisi bilan har bir kalit bo'yicha Object.is da solishtiradi. Hammasi teng bo'lsa — bolani chizmaydi, eski natijani ishlatadi. Uchta qator, uchta xil props:
import { memo, useState } from "react";
const DishRow = memo(function DishRow({ dish, style, onPick }) {
console.log("DishRow:", dish);
return (
<li style={style} onClick={onPick}>
{dish}
</li>
);
});
function App() {
const [count, setCount] = useState(0);
console.log("App:", count);
return (
<div>
<button onClick={() => setCount(count + 1)}>Sanagich</button>
<ul>
<DishRow dish="Osh" />
<DishRow dish="Manti" style={{ color: "red" }} />
<DishRow dish="Somsa" onPick={() => console.log("Somsa")} />
</ul>
</div>
);
}Konsolda:
App: 0
DishRow: Osh
DishRow: Manti
DishRow: Somsa
App: 1
DishRow: Manti
DishRow: Somsa| Qator | Props | memo ishladimi |
Nega |
|---|---|---|---|
| Osh | dish="Osh" |
ha | satr — qiymat bo'yicha teng |
| Manti | style={{…}} |
yo'q | har renderda yangi obyekt |
| Somsa | onPick={() => …} |
yo'q | har renderda yangi funksiya |
Uchala qator ham memo da. Lekin faqat birinchisi himoyalandi. Ikkinchi va uchinchi qatorda memo foydasiz — undan ham yomoni: React har safar props'ni solishtiradi (bekor ish) va baribir chizadi.
3.2 Uch tuzatish
| Muammo | Tuzatish |
|---|---|
doimiy obyekt (style={{ color: "red" }}) |
komponentdan tashqarida konstanta: const RED = { color: "red" } |
| hisoblangan obyekt yoki massiv | useMemo |
| funksiya | useCallback yoki funksiyani bolaga bermaslik |
Birinchisi eng oddiy va eng ko'p unutiladigan: state'ga bog'liq bo'lmagan obyekt komponentdan tashqarida bir marta yasaladi. Qolgan ikkitasi — keyingi bo'limlarda.
3.3 Custom compare — ehtiyot bo'ling
memo ga ikkinchi argument berish mumkin — o'z solishtirish funksiyangiz: memo(Component, arePropsEqual). true qaytarsa — "teng, chizma". Vasvasa katta: "funksiya props'ni e'tiborsiz qoldiraman, shunda useCallback kerak emas". Natija:
import { memo, useState } from "react";
const DishButton = memo(
function DishButton({ dish, onOrder }) {
console.log("DishButton chizildi");
return <button onClick={onOrder}>{dish}</button>;
},
(prev, next) => prev.dish === next.dish, // ❌ onOrder e'tiborsiz
);
function App() {
const [discount, setDiscount] = useState(0);
function order() {
console.log("buyurtma, chegirma:", discount);
}
return (
<div>
<button onClick={() => setDiscount(10)}>Aksiya</button>
<DishButton dish="Osh" onOrder={order} />
</div>
);
}Konsolda:
DishButton chizildi
buyurtma, chegirma: 0«Aksiya» bosildi — chegirma 10. Lekin buyurtma chegirma: 0 bilan ketdi. DishButton qayta chizilmadi, demak uning <button onClick> ida birinchi renderdagi order qoldi — u o'z snapshot'idagi discount = 0 ni ko'radi (State II: snapshot). Bu eskirgan closure (stale closure) — haqiqiy pul xatosi bo'lishi mumkin.
React hujjati (memo, "Specifying a custom comparison function") ham shuni ogohlantiradi: custom compare'da har props'ni, ayniqsa funksiyalarni solishtiring; chuqur tenglik esa sekin va ilovani qotirib qo'yishi mumkin. Amalda custom compare kam kerak — avval props'ni barqaror qiling.
Tekshirib ko'ring:
memoliMenuCardgaprice={35000}vatags={["achchiq"]}berilgan. Ota qayta chizilsa,MenuCardchiziladimi?
Javob
Chiziladi. price — son, teng. tags — har renderda yangi massiv, Object.is — false. Bitta teng bo'lmagan props yetarli. Tuzatish: tags doimiy bo'lsa — komponentdan tashqarida const SPICY = ["achchiq"]; hisoblangan bo'lsa — useMemo.
4. useMemo
4.1 Qimmat hisobni saqlash
useMemo(hisob, massiv) — birinchi renderda hisob() ni chaqiradi va natijani saqlaydi. Keyingi renderlarda massivdagi qiymatlar (Object.is bilan) o'zgarmagan bo'lsa — hisobni chaqirmaydi, o'sha natijani qaytaradi:
import { useMemo, useState } from "react";
const MENU = ["Osh", "Manti", "Somsa", "Lag'mon"];
let calls = 0;
function filterMenu(items, query) {
calls += 1;
console.log("filterMenu chaqirildi:", calls);
return items.filter((d) => d.toLowerCase().includes(query));
}
function App() {
const [query, setQuery] = useState("");
const [tick, setTick] = useState(0);
const found = useMemo(() => filterMenu(MENU, query), [query]);
console.log("App:", tick, found.length);
return (
<div>
<input
placeholder="Qidiruv"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<button onClick={() => setTick(tick + 1)}>Yangila</button>
<p>{found.join(", ")}</p>
</div>
);
}Konsolda:
filterMenu chaqirildi: 1
App: 0 4
App: 1 4
filterMenu chaqirildi: 2
App: 1 3
App: 2 3Ekranda:
Yangila
Osh, Somsa, Lag'monTo'rt render, ikki hisob. «Yangila» — query o'zgarmadi, filterMenu chaqirilmadi. Qidiruvga "o" yozildi — query o'zgardi, hisob qayta bajarildi. Haqiqiy loyihada filterMenu 10 000 elementli ro'yxatni aylanib chiqsa, tejash sezilarli bo'ladi.
4.2 Barqaror havola
useMemo ning ikkinchi — va React'da ko'pincha asosiy — vazifasi: havolani barqaror qilish. found — massiv. Uni memo li bolaga bersangiz, useMemo siz u har renderda yangi bo'lardi va memo buzilardi. useMemo bilan esa query o'zgarmaguncha — o'sha massiv.
useDeferredValue darsidagi vazifalar kodida aynan shu sabab yozilgan edi: korinadigan — useMemo, chunki u memo(VazifaRoyxati) ga props bo'lib boradi. Hisobni tejash ikkinchi darajali edi.
4.3 Kafolat emas — optimallashtirish
React hujjati (useMemo, "Caveats") ikki narsani aytadi:
- Dev rejimda StrictMode hisob funksiyasini ikki marta chaqiradi — toza bo'lmasa, xato darhol ko'rinsin (StrictMode chuqur).
- React keshni ba'zi hollarda tashlab yuborishi mumkin va kelajakda tashlashi ham mumkin. Shuning uchun
useMemoni "faqat bir marta bajarilsin" kafolati sifatida ishlatmang. Bunday kerak bo'lsa —useState(() => …)yokiuseRef.
Qoida: useMemo ni olib tashlasangiz, kod sekinlashishi mumkin, lekin buzilmasligi kerak.
Tekshirib ko'ring:
const total = useMemo(() => price * qty, [price, qty]);— buuseMemofoydalimi?
Javob
Deyarli yo'q. Ko'paytirish — nanosekundlar, useMemo esa har renderda massivni solishtiradi va keshni saqlaydi: tejashdan ko'ra xarajati katta bo'lishi mumkin. Natija — son (primitiv), havola barqarorligi ham kerak emas. Shunchaki const total = price * qty;.
5. useCallback
5.1 Funksiya uchun useMemo
useCallback(fn, massiv) — massiv o'zgarmaguncha o'sha funksiyani qaytaradi. Aslida bu useMemo(() => fn, massiv) ning qisqa yozuvi. U funksiyani tezlashtirmaydi va "kamroq yaratmaydi" (inline funksiya baribir har renderda yasaladi, faqat qaytarilmaydi). Yagona foydasi — barqaror havola:
import { memo, useCallback, useState } from "react";
const DishButton = memo(function DishButton({ dish, onOrder }) {
console.log("DishButton chizildi:", dish);
return <button onClick={() => onOrder(dish)}>{dish}</button>;
});
function App() {
const [count, setCount] = useState(0);
const [orders, setOrders] = useState([]);
const handleOrder = useCallback((dish) => {
setOrders((prev) => [...prev, dish]);
}, []);
console.log("App:", count, "buyurtma:", orders.length);
return (
<div>
<button onClick={() => setCount(count + 1)}>Sanagich</button>
<DishButton dish="Osh" onOrder={handleOrder} />
<DishButton dish="Manti" onOrder={handleOrder} />
</div>
);
}Konsolda:
App: 0 buyurtma: 0
DishButton chizildi: Osh
DishButton chizildi: Manti
App: 1 buyurtma: 0
App: 1 buyurtma: 1«Sanagich» ham, «Osh» (buyurtma qo'shildi) ham App ni chizdi — lekin tugmalar chizilmadi. handleOrder — o'sha funksiya, dish — o'sha satr.
5.2 Bo'sh massiv va updater
Diqqat qiling: useCallback(…, []) — massiv bo'sh, lekin funksiya baribir eng so'nggi buyurtmalar ro'yxatiga qo'shadi. Sir — updater funksiya: setOrders((prev) => [...prev, dish]) (State II). Funksiya orders ni o'qimaydi — React eng so'nggisini prev qilib beradi. Agar setOrders([...orders, dish]) deb yozsangiz, orders massivga kirishi kerak bo'lardi va funksiya har buyurtmada yangilanardi.
set… funksiyalarining o'zi doim barqaror — ularni massivga yozish shart emas (State I). ESLint'ning react-hooks/exhaustive-deps qoidasi qolganini nazorat qiladi (useEffect II): massivdan biror narsani unutsangiz — eskirgan closure, ortiqcha qo'shsangiz — keraksiz yangilanish.
flowchart TD
A{"Bola memo'dami?"} -->|"yo'q"| B["useCallback/useMemo foydasiz"]
A -->|"ha"| C{"Props'da obyekt yoki funksiya?"}
C -->|"yo'q"| D["memo o'zi yetadi"]
C -->|"ha"| E{"State'ga bog'liqmi?"}
E -->|"yo'q"| F["Komponentdan tashqariga"]
E -->|"ha"| G["useMemo / useCallback"]Diagrammaning birinchi tuguniga qarang: memo siz bolaga useCallback berish — eng ko'p uchraydigan foydasiz optimallashtirish. Bola baribir chiziladi ("ota chizildi").
6. Qachon kerak emas
6.1 Erta optimallashtirish
Uchala vosita ham bepul emas:
| Narx | Qayerda |
|---|---|
| props yoki massiv har renderda solishtiriladi | memo, useMemo, useCallback |
| eski natija xotirada turadi | kesh |
| kod o'qilishi qiyinlashadi, massivlar xatoga sabab | eskirgan closure, unutilgan bog'liqlik |
| bitta yangi obyekt butun zanjirni buzadi | ishonch noto'g'ri bo'ladi |
Premature optimization (erta optimallashtirish) — o'lchanmagan muammoni "har ehtimolga qarshi" davolash. Natija: murakkab kod, sezilmaydigan foyda. Bu darsdagi barcha misollar console.log bilan o'lchangan; vazifalar dagi qaror esa Event Timing va render soni bilan.
6.2 Qachon kerak
React hujjati (useMemo, "Should you add useMemo everywhere?") va kanon tajribasi bo'yicha — uchta holat:
- Hisob haqiqatan qimmat —
console.timeyoki Profiler 1 ms dan ko'p ko'rsatsa (sekin telefonda bu yana bir necha barobar). - Natija
memoli bolaga props bo'lib boradi va u bola qimmat yoki ko'p. - Natija boshqa hook'ning massivida (
useEffect, boshqauseMemo) — har renderda yangi bo'lsa, effekt har renderda ishlaydi.
Qolgan hollarda — oddiy kod. Keyingi darsdagi React Compiler bu tanlovni avtomatlashtirishga urinadi; u bilan nima o'zgarishini o'lchaymiz.
7. Vazifalar qadami: qator memo si (holat 20)
7.1 Ikki sabab, ikki tuzatish
Yangi branch: perf/qator-memo. Qayta render sabablari darsidagi tashxis — ikki qism:
VazifaQatorimemosiz — "ota chizildi" bilan 1 000 qator chiziladi.memoqo'shilsa ham,korinadiganVazifalar()har chaqirilganda yangiVazifaobyektlarini qaytaradi —vazifaprops'i doim "yangi".
Ikkinchisi birinchi tuzatiladi — aks holda memo «Uchta qator, uchta xil props» dagi Manti qatoriga o'xshab foydasiz bo'ladi. Diff: 4 fayl, +50 −13.
7.2 holat.ts: o'sha obyektlar
// Filtr, keyin qidiruv: qidir tartibni saqlaydi.
// 18/#20: natija — ro'yxatdagi O'SHA obyektlar (Vazifa nusxalari
// emas): o'zgarmagan vazifa havolasi o'zgarmaydi — memo'li qator
// (VazifaQatori) uni Object.is bilan solishtirib, qayta chizilmaydi.
// Avval har renderda yangi Vazifa yasalardi va 1 000 qatorning
// hammasi qayta chizilardi (o'lchandi: bitta katak — 18 056
// komponent render, natijalar/18-*)
export function korinadiganVazifalar<T extends VazifaMalumoti>(
vazifalar: readonly T[],
filtr: Filtr,
saralash: Saralash,
qidiruv: string,
): T[] {
const asl = new Map(vazifalar.map((v) => [v.id, v]));
return qidir(
VazifalarRoyxati.dan(vazifalar).korinadiganlar(filtr, saralash),
qidiruv,
// id lar bir xil — doim topiladi
).flatMap((v) => asl.get(v.id) ?? []);
}Izoh qatorlari telefon uchun qayta bo'lindi (oxirgi qatordagi izoh uning ustiga ko'chdi), kod — aynan. Uch narsa:
- Filtr, saralash va qidiruv mantig'i o'zgarmadi —
VazifalarRoyxatiklassi (vazifalarv2) ishlayveradi. Faqat oxirida: har natija id bo'yicha asl obyektga almashtiriladi. new Map(…)— id → asl obyekt; qidirish O(1) (Map).flatMap(… ?? [])— topilmasa (bo'lmaydi, lekin TypeScript bilmaydi) — bo'sh massiv, ya'ni tashlab yuboriladi.- Generic
<T extends VazifaMalumoti>(Generic cheklovlar): qaytgan tur — kirgan tur. AvvalVazifa[]qaytardi, endi —readonly T[]dagiTning o'zi. Tur ham "o'sha obyekt" ekanini aytadi.
Test ham aynan shuni tekshiradi (tekshiruv/holat.test.js, +1):
test("18/#20 korinadiganVazifalar — ro'yxatdagi O'SHA obyektlar (memo uchun)", () => {
const natija = korinadiganVazifalar(
boshlangichVazifalar,
"hammasi",
"standart",
"",
);
assert.equal(
natija.every((v) => boshlangichVazifalar.includes(v)),
true,
);
});includes — === bilan qidiradi: har natija asl massivdagi o'sha obyekt bo'lishi kerak. Mazmunan teng nusxa testdan o'tmaydi.
7.3 VazifaQatori: memo
// memo (18/#20): props — vazifa (ro'yxatdagi o'sha obyekt, #20),
// sorov (satr), tahrirda (boolean) — o'zgarmagan qator qayta
// chizilmaydi. Hodisalar context'dan — u barqaror (18/#02),
// memo'ni buzmaydi
export const VazifaQatori = memo(function VazifaQatori({
vazifa,
sorov,
tahrirda,
}: Props) {Izohda butun tekshiruv ro'yxati: uchta props, uchalasi barqaror. vazifa — o'sha obyekt (yuqoridagi tuzatish), sorov — satr, tahrirda — boolean. Qator hodisalari props'da emas, context'da — u esa #02 dan beri barqaror ("latest ref", useDeferredValue). Ya'ni «Uchta qator, uchta xil props» dagi uchala xavf ham yopilgan.
memo(function VazifaQatori(…)) — nomli funksiya: DevTools va xato izlarida "Anonymous" emas, VazifaQatori ko'rinadi.
7.4 App.tsx: hosilaviy qiymatlar useMemo da
const royxat = useMemo(
() => VazifalarRoyxati.dan(vazifalar),
[vazifalar],
);
…
// 18/#20: ro'yxatdan hisoblanadigan hamma narsa — faqat
// ro'yxat o'zgarganda (qidiruvda har harfda emas). Teglar
// Trie'si — O(T · l)
const { qoldi, bajarildi, teglar, yopilgan, daraxt } = useMemo(() => {
const teglar = royxat.tegHisobi();
return {
...royxat.statistika(),
teglar,
yopilgan: royxat.yopilganTeglar(),
daraxt: tegDaraxtiniQur(teglar),
};
}, [royxat]);Bu — «Qimmat hisobni saqlash» holati. Statistika, teg hisobi va teglar daraxti (Trie, Trie) — 10 000 vazifada sezilarli ish. Ular faqat vazifalar ga bog'liq, lekin avval har renderda — qidiruvning har harfida ham — qayta hisoblanardi. Endi — faqat ro'yxat o'zgarganda. yopilgan={royxat.yopilganTeglar()} props'i ham shu obyektga ko'chdi.
7.5 O'lchov
O'sha olchov/renderlar.mjs, 1 000 vazifa (natijalar/20-renderlar.txt, holat 12 bilan bir xil sharoit):
- holat 12: memo faqat ro'yxatdaVazifaQatori×300018 056 render
- holat 20: asl obyektlar + memo(VazifaQatori)VazifaQatori×068 render
Manba: O'lchandi: vazifalar holat 12 va 20, olchov/renderlar.mjs, Chrome headless, soxta server, 2026-10-13
| Ssenariy | Holat 12 | Holat 20 |
|---|---|---|
| katak | 18 056 | 68 |
| yangi vazifa qo'shish | 18 068 | 68 |
| qidiruv: "vazifa 12" (9 harf) | 237 | 927 |
| qidiruv: bitta harf "v" | 6 035 | 6 035 |
| e'lon o'chishi | 17 | 17 |
Katakda 68 render qoldi — App, XatoChegarasi, StatistikaPaneli, forma, filtrlar va o'zgargan qatorning o'zi (AmalTugmasi×4). 999 qator — nol.
Qidiruvdagi o'sish (237 → 927) ham bor. Qatorlar soniga qarang: holat 12 da VazifaQatori×11, holat 20 da ×123. Ro'yxatdagi 1 000 vazifadan "vazifa 12" ga 11 tasi mos keladi, "vazifa 1" ga esa 112 tasi (1, 10–19, 100–199, 1000). 123 = 112 + 11 — juda mos keladi.
Bizning tushuntirishimiz shunday. Holat 20 dagi o'lchovda tez yozish paytida oraliq so'rov "vazifa 1" ham ekranga chiqdi. Holat 12 dagisida esa React uni tashlab, darhol yakuniy so'rovga o'tdi (useDeferredValue). Lekin bu — taxmin, isbot emas. Xom faylda faqat jami son bor, qaysi so'rov commit bo'lgani yozilmagan. Aniq bilish uchun har commit'dagi so'rovni ham yozib olish kerak edi. Shuning uchun bu raqamdan "memo qidiruvni sekinlashtirdi" degan xulosa chiqarmang. Qidiruvning eng og'ir holati ("v" — 6 035) esa o'zgarmadi: har qatorda belgilangan matn haqiqatan o'zgaradi, memo buni to'xtatmaydi va to'xtatmasligi kerak.
Vaqt (Event Timing, bosishdan kadrgacha, 20-oldin.txt → 20-keyin.txt):
- 1 000 — holat 1248 ms
- 1 000 — holat 2032 ms
- 10 000 — holat 12264 ms
- 10 000 — holat 20120 ms
Manba: O'lchandi: vazifalar holat 12 va 20, olchov/katak-tezligi.mjs (Event Timing), Chrome headless, 1 000 vazifada 18 bosish, 10 000 da 12 bosish, 2026-10-13
10 000 vazifada 264 → 120 ms: INP "yaxshi emas" dan (200 ms dan ko'p) "yaxshi" ga o'tdi. Qolgan 120 ms — 10 000 elementli DOM'ning o'zi va App dagi qolgan hisoblar; ular Virtualizatsiya darsida.
7.6 Tekshiruvlar va commit
Testlar: node 164 (+1 — «o'sha obyektlar» testi), Vitest 39; lint, format:check, tip — toza (natijalar/tekshiruv-20.txt). Brauzer: 159/159, e2e-18 36/36. Build: index JS 254.86 KB (gzip 80.71 KB) — memo va useMemo hajmga deyarli ta'sir qilmaydi.
perf: qator memo — ro'yxatdagi o'sha obyektlar, hosilaviy useMemoCommit tanasida: korinadiganVazifalar — asl obyektlar (Map + flatMap, generic); memo(VazifaQatori); royxat va hosilaviy qiymatlar — useMemo([vazifalar]); o'lchov: katak 18 056 → 68 render, 1 000: 48 → 32 ms, 10 000: 264 → 120 ms.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
memo bor, bola baribir chiziladi |
props'da inline obyekt, massiv yoki funksiya | konstanta, useMemo, useCallback |
useCallback bor, foyda yo'q |
bola memo siz |
memo qo'shing yoki useCallback ni olib tashlang |
| tugma eski qiymat bilan ishlaydi | custom compare funksiyani e'tiborsiz qoldirdi | hamma props'ni solishtiring yoki compare'siz |
useCallback har renderda yangilanadi |
massivda har renderda yangi qiymat | updater funksiya, massivni qisqartirish |
ro'yxat memo da, qatorlar baribir chiziladi |
map da yangi obyektlar ({ ...v }, klass nusxasi) |
asl obyektlarni bering |
useMemo ichida fetch yoki setState |
hisob — toza bo'lishi kerak | effekt yoki hodisa |
9. Mashqlar
Mashqlar kurs/mashqlar/18/20-memo/ da. Har yechimni «Ishga tushir» bilan sinang.
1-mashq (oson): teng yoki yo'q?
memo uchun props teng hisoblanadimi (ota qayta chizilganda)?
count={5}→count={5}:user={{ name: "Ali" }}(JSX ichida) → o'sha yozuv:onClick={handleClick}(handleClick— komponent ichida oddiyfunction):items={MENU}(MENU— modul darajasidagi massiv):
Yechim
Son — qiymat bo'yicha teng. JSX ichidagi {{ … }} — har renderda yangi obyekt. Komponent ichidagi function handleClick ham har renderda yangidan e'lon qilinadi — yangi funksiya. Modul darajasidagi MENU esa bir marta yasalgan — o'sha havola.
2-mashq (o'rta): rangli qator
PriceTag — memo da, lekin «Sanagich» har bosilganda chizilyapti. Avval qaysi props "yangi" ekanini toping, keyin tuzating (useMemo siz, useCallback siz): «Sanagich» PriceTag ni chizmasin. Kutilgan konsol — pastda.
import { memo, useState } from "react";
const PriceTag = memo(function PriceTag({ price, style }) {
console.log("PriceTag chizildi");
return <p style={style}>{price} so'm</p>;
});
function App() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Sanagich</button>
<PriceTag price={35000} style={{ fontWeight: "bold" }} />
</div>
);
}Konsolda:
PriceTag chizildi
PriceTag chizildi
PriceTag chizildiYechim
import { memo, useState } from "react";
const BOLD = { fontWeight: "bold" };
const PriceTag = memo(function PriceTag({ price, style }) {
console.log("PriceTag chizildi");
return <p style={style}>{price} so'm</p>;
});
function App() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Sanagich</button>
<PriceTag price={35000} style={BOLD} />
</div>
);
}Konsolda:
PriceTag chizildiBitta joy yetdi: stil obyekti komponentdan tashqariga, BOLD konstantasiga chiqdi — u state'ga bog'liq emas. price={35000} — son, u allaqachon teng edi. memo ni ham, price ni ham o'zgartirish kerak emas. Doim avval qaysi props "yangi" ekanini aniqlang.
3-mashq (qiyin): savat va barqaror ishlovchi
CartItem — memo da. App da savat (items state'i) va remove(id) funksiyasi. Hozir bitta taom o'chirilganda qolgan hamma qatorlar qayta chiziladi. Shunday qiling: o'chirishda qolgan qatorlar chizilmasin, «Sanagich» da esa hech qaysi qator chizilmasin. Ishora: useCallback + updater funksiya («Bo'sh massiv va updater» bo'limi).
import { memo, useState } from "react";
const CartItem = memo(function CartItem({ item, onRemove }) {
console.log("CartItem:", item.dish);
return (
<li>
{item.dish}{" "}
<button onClick={() => onRemove(item.id)}>×</button>
</li>
);
});
function App() {
const [items, setItems] = useState([
{ id: 1, dish: "Osh" },
{ id: 2, dish: "Manti" },
{ id: 3, dish: "Somsa" },
]);
const [count, setCount] = useState(0);
function remove(id) {
setItems(items.filter((i) => i.id !== id));
}
return (
<div>
<button onClick={() => setCount(count + 1)}>Sanagich</button>
<ul>
{items.map((item) => (
<CartItem key={item.id} item={item} onRemove={remove} />
))}
</ul>
</div>
);
}Yechim
import { memo, useCallback, useState } from "react";
const CartItem = memo(function CartItem({ item, onRemove }) {
console.log("CartItem:", item.dish);
return (
<li>
{item.dish}{" "}
<button onClick={() => onRemove(item.id)}>×</button>
</li>
);
});
function App() {
const [items, setItems] = useState([
{ id: 1, dish: "Osh" },
{ id: 2, dish: "Manti" },
{ id: 3, dish: "Somsa" },
]);
const [count, setCount] = useState(0);
const remove = useCallback((id) => {
setItems((prev) => prev.filter((i) => i.id !== id));
}, []);
return (
<div>
<button onClick={() => setCount(count + 1)}>Sanagich</button>
<ul>
{items.map((item) => (
<CartItem key={item.id} item={item} onRemove={remove} />
))}
</ul>
</div>
);
}Konsolda:
CartItem: Osh
CartItem: Manti
CartItem: SomsaEkranda:
Sanagich
Manti ×
Somsa ×Birinchi chizishdan keyin — bironta ham CartItem logi yo'q: «Sanagich» da remove o'sha funksiya, item lar o'sha obyektlar. Birinchi «×» (Osh) bosilganda Osh o'chdi, Manti va Somsa chizilmadi: filter yangi massiv qaytaradi, lekin ichidagi qolgan obyektlar — o'sha. Bu vazifalar dagi «o'sha obyektlar» qoidasining kichik nusxasi. prev — eng so'nggi savat, shuning uchun massiv bo'sh va funksiya hech qachon eskirmaydi.
4-mashq: vazifalar qadami
perf/qator-memo branch'ida «Vazifalar qadami» dagi to'rt o'zgarishni kiriting. Ular: korinadiganVazifalar (asl obyektlar, generic), memo(VazifaQatori), royxat va hosilaviy qiymatlar useMemo da, holat.test.js dagi yangi test. npm test, npm run lint, npm run tip toza bo'lsin. React DevTools Profiler bilan bitta katakni yozib oling — commit va PR.
Yechim
Testlar: node 164, Vitest 39. Profiler'da katak bosilganda flamegraph'da VazifaRoyxati sariq (chizildi), uning ostidagi 1 000 qatordan faqat bittasi rangli, qolganlari kulrang ("Did not render"). Agar hammasi rangli bo'lsa — "Why did this render?" ni o'qing: "• Props changed:" va yonida vazifa — korinadiganVazifalar hali nusxa qaytaryapti; "• Context changed" — qator context'i barqaror emas (#02 dagi "latest ref" tekshiring). Commit xabari — «Tekshiruvlar va commit» bo'limida.
10. Real ishda
- Jadvallar va ro'yxatlar: admin panel, CRM, chat —
memoli qator + barqaror ma'lumot obyektlari — eng ko'p uchraydigan haqiqiy foydalimemo. - State kutubxonalari: Redux va Zustand "o'zgarmagan qism — o'sha havola" qoidasiga qurilgan (immutable yangilash) —
memoular bilan tabiiy ishlaydi. - Kod ko'rigi: "bu
useCallbackqaysimemoli bolaga ketyapti?" — javob bo'lmasa, uni olib tashlash taklif qilinadi. - Intervyu savollari: "
memo,useMemo,useCallbackfarqi?", "Negamemoishlamay qoldi?", "useCallbackfunksiyani tezlashtiradimi?", "Hamma joygamemoqo'yish yaxshimi?".
Xulosa
Object.is: primitiv — qiymat bo'yicha, obyekt/massiv/funksiya — havola bo'yicha; render har safar yangi literallar yasaydi.memo— props'ni kalitma-kalit solishtiradi; bitta inline obyekt yoki funksiya uni foydasiz qiladi. Custom compare funksiyalarni e'tiborsiz qoldirsa — eskirgan closure.useMemo— qimmat hisob va barqaror havola uchun; kafolat emas, optimallashtirish.useCallback— funksiya uchunuseMemo, faqatmemoli bola yoki hook massivi uchun kerak.- Tartib: o'lchov → kompozitsiya →
memo+ barqaror props. Erta optimallashtirish — murakkablik, foydasiz. vazifalarholat 20: asl obyektlar +memo(VazifaQatori)+ hosilaviyuseMemo— katak 18 056 → 68 render, 1 000: 48 → 32 ms, 10 000: 264 → 120 ms.
Keyingi dars: React Compiler: avtomatik memoizatsiya — kompilyator bularni o'zi qo'ysa-chi? vazifalar da o'lchab ko'ramiz va nega kanonga kirmaganini bilamiz.
Manbalar
- React:
memo— react.dev/reference/react/memo - React:
useMemo— react.dev/reference/react/useMemo - React:
useCallback— react.dev/reference/react/useCallback - Kanon:
sinov/18/QADAMLAR.md(#20),natijalar/20-renderlar.txt,natijalar/20-oldin.txt,natijalar/20-keyin.txt,natijalar/tekshiruv-20.txt
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!