17-bo‘lim
Unumdorlik
React qachon qayta chizadi, memo, useMemo va useCallback, ularni qachon ishlatmaslik va haqiqiy sekinlik sabablari.
Ushbu bo‘lim mundarijasi
React odatda yetarlicha tez. Lekin ilova o'sgani sari ba'zi joylar sekinlashishi mumkin.
Bu bo'lim uch vositani ko'rsatadi - va ularni qachon ishlatmaslik kerakligini ham.
React qachon qayta chizadi #
import { useState } from "react";
import { chiz, bos } from "./dom-muhit.mjs";
const izlar = [];
function Bola() {
izlar.push("Bola chizildi");
return <p>bola</p>;
}
function Ota() {
const [soni, ozgartir] = useState(0);
izlar.push(`Ota chizildi: ${soni}`);
return (
<div>
<button onClick={() => ozgartir(soni + 1)}>Oshirish</button>
<Bola />
</div>
);
}
await chiz(<Ota />);
izlar.push("--- bosildi ---");
await bos("button");
console.log(izlar.join("\n"));
Ota chizildi: 0
Bola chizildi
--- bosildi ---
Ota chizildi: 1
Bola chizildi
Bola ning props i yo'q va u umuman o'zgarmadi - lekin baribir
qayta chizildi.
Bu juda muhim farq va ko'pchilik buni bilmaydi.
"Qayta chizish" - komponent funksiyasi yana chaqirilishi degani. U yangi JSX qaytaradi, React esa uni eskisi bilan solishtiradi.
Agar natija bir xil bo'lsa, React DOM ga hech narsa
tegmaydi. Ya'ni yuqoridagi misolda <p>bola</p> qayta
hisoblandi, lekin brauzerdagi element o'zgarmadi.
Funksiyani qayta chaqirish arzon. Qimmat narsa - DOM ni o'zgartirish, va React uni allaqachon minimallashtiradi.
Shuning uchun "ortiqcha qayta chizish" odatda muammo emas. U faqat komponent haqiqatan og'ir bo'lganda bilinadi.
memo #
import { useState, memo } from "react";
import { chiz, bos } from "./dom-muhit.mjs";
const izlar = [];
const Eslangan = memo(function Eslangan({ matn }) {
izlar.push(`Eslangan chizildi: ${matn}`);
return <p>{matn}</p>;
});
function Oddiy({ matn }) {
izlar.push(`Oddiy chizildi: ${matn}`);
return <p>{matn}</p>;
}
function Ota() {
const [soni, ozgartir] = useState(0);
return (
<div>
<button onClick={() => ozgartir(soni + 1)}>Oshirish</button>
<Eslangan matn="o'zgarmas" />
<Oddiy matn="o'zgarmas" />
</div>
);
}
await chiz(<Ota />);
izlar.push("--- bosildi ---");
await bos("button");
console.log(izlar.join("\n"));
Eslangan chizildi: o'zgarmas
Oddiy chizildi: o'zgarmas
--- bosildi ---
Oddiy chizildi: o'zgarmas
memo bilan o'ralgan komponent props o'zgarmagan bo'lsa,
qayta chizilmaydi.
memo props ni yuzaki (shallow) solishtiradi: har bir
props ni Object.is bilan.
memo ni obyekt buzadi #
import { useState, memo } from "react";
import { chiz, bos } from "./dom-muhit.mjs";
const izlar = [];
const Eslangan = memo(function Eslangan({ sozlama }) {
izlar.push(`chizildi: ${sozlama.matn}`);
return <p>{sozlama.matn}</p>;
});
function Ota() {
const [soni, ozgartir] = useState(0);
const sozlama = { matn: "o'zgarmas" };
return (
<div>
<button onClick={() => ozgartir(soni + 1)}>Oshirish</button>
<Eslangan sozlama={sozlama} />
</div>
);
}
await chiz(<Ota />);
izlar.push("--- bosildi ---");
await bos("button");
console.log(izlar.join("\n"));
chizildi: o'zgarmas
--- bosildi ---
chizildi: o'zgarmas
memo ishlamadi.
const sozlama = { matn: "o'zgarmas" } har chizishda yangi
obyekt yasaydi. Mazmuni bir xil, lekin havola boshqa.
memo havolani solishtiradi - shuning uchun "props o'zgardi"
deb xulosa qiladi.
Xuddi shu muammo funksiyalarda ham bor:
<Eslangan onBos={() => ishla()} />
Bu ham har chizishda yangi funksiya.
Aynan shu sababdan memo ko'pincha useMemo va useCallback
bilan birga ishlatiladi - ular quyida.
Va aynan shu sababdan memo ni shunchaki qo'shib qo'yish
foyda bermaydi: props barqaror bo'lmasa, u faqat qo'shimcha
tekshiruv qo'shadi.
useMemo #
import { useState, useMemo } from "react";
import { chiz, bos, matn } from "./dom-muhit.mjs";
let hisoblashlar = 0;
function ogirHisob(royxat) {
hisoblashlar += 1;
return royxat.reduce((a, b) => a + b, 0);
}
function Hisobot({ sonlar }) {
const [boshqa, ozgartir] = useState(0);
const yigindi = useMemo(() => ogirHisob(sonlar), [sonlar]);
return (
<div>
<p id="natija">Yig'indi: {yigindi}, boshqa: {boshqa}</p>
<button onClick={() => ozgartir(boshqa + 1)}>Boshqasi</button>
</div>
);
}
const sonlar = [1, 2, 3, 4, 5];
await chiz(<Hisobot sonlar={sonlar} />);
console.log(matn("#natija"), "| hisoblashlar:", hisoblashlar);
await bos("button");
await bos("button");
console.log(matn("#natija"), "| hisoblashlar:", hisoblashlar);
Yig'indi: 15, boshqa: 0 | hisoblashlar: 1
Yig'indi: 15, boshqa: 2 | hisoblashlar: 1
Ikki marta bosildi va komponent ikki marta qayta chizildi, lekin og'ir hisob bir martagina bajarildi.
useMemo(() => ogirHisob(sonlar), [sonlar]) natijani eslab
qoladi va sonlar o'zgarmagani uchun uni qayta ishlatdi.
useMemo siz bu son har bosishda oshardi: komponent qayta
chizilganda ogirHisob ham qaytadan chaqirilardi.
useCallback #
import { useState, useCallback, memo } from "react";
import { chiz, bos } from "./dom-muhit.mjs";
const izlar = [];
const Tugma = memo(function Tugma({ bosildi, nom }) {
izlar.push(`${nom} chizildi`);
return <button onClick={bosildi}>{nom}</button>;
});
function Ota() {
const [soni, ozgartir] = useState(0);
const barqaror = useCallback(() => {}, []);
const beqaror = () => {};
return (
<div>
<button id="asosiy" onClick={() => ozgartir(soni + 1)}>
Oshirish
</button>
<Tugma nom="barqaror" bosildi={barqaror} />
<Tugma nom="beqaror" bosildi={beqaror} />
</div>
);
}
await chiz(<Ota />);
izlar.push("--- bosildi ---");
await bos("#asosiy");
console.log(izlar.join("\n"));
barqaror chizildi
beqaror chizildi
--- bosildi ---
beqaror chizildi
useCallback funksiyani eslab qoladi va bog'liqliklar
o'zgarmasa, o'sha funksiyani qaytaradi.
Shuning uchun memo bilan o'ralgan barqaror tugma qayta
chizilmadi.
| Ilgak | Nimani eslaydi |
|---|---|
useMemo | Hisoblash natijasini |
useCallback | Funksiyaning o'zini |
useCallback(f, []) - bu useMemo(() => f, []) ning qisqa
shakli.
Ularni qachon ishlatmaslik kerak #
import { useMemo } from "react";
import { renderToStaticMarkup } from "react-dom/server";
function Yomon({ a, b }) {
const yigindi = useMemo(() => a + b, [a, b]);
return <p>{yigindi}</p>;
}
function Yaxshi({ a, b }) {
const yigindi = a + b;
return <p>{yigindi}</p>;
}
console.log(renderToStaticMarkup(<Yomon a={2} b={3} />));
console.log(renderToStaticMarkup(<Yaxshi a={2} b={3} />));
<p>5</p>
<p>5</p>
useMemo(() => a + b, [a, b]) a + b dan sekinroq.
Sababi: useMemo ning o'zi ham ish talab qiladi - bog'liqliklarni
saqlash, ularni solishtirish, natijani xotirada tutish. Ikki
sonni qo'shish esa deyarli bepul.
Uchta zarari bor:
- Sekinroq - qo'shimcha tekshiruv;
- Ko'proq xotira - har bir eslangan qiymat saqlanadi;
- O'qish qiyinroq - kod shovqinga to'ladi.
Qoida: useMemo va useCallback ni o'lchamdan keyin
qo'shing, oldin emas.
Ular haqiqatan kerak bo'lgan uchta holat:
- Hisoblash haqiqatan og'ir (minglab elementni saralash, filtrlash);
- Natija
memobilan o'ralgan komponentga prop sifatida ketadi; - Qiymat
useEffectning bog'liqligi (10-bo'limdagi cheksiz sikl).
Ro'yxatni filtrlash #
import { useState, useMemo } from "react";
import { chiz, yoz, kut, matn } from "./dom-muhit.mjs";
let filtrlashlar = 0;
const talabalar = Array.from({ length: 500 }, (_, i) => ({
id: i,
ism: `Talaba-${i}`,
}));
function Royxat() {
const [so, ozgartir] = useState("");
const [boshqa, boshqaniOzgartir] = useState(0);
const topilgan = useMemo(() => {
filtrlashlar += 1;
return talabalar.filter((t) => t.ism.includes(so));
}, [so]);
return (
<div>
<input value={so} onChange={(h) => ozgartir(h.target.value)} />
<button onClick={() => boshqaniOzgartir(boshqa + 1)}>
Boshqasi
</button>
<p id="soni">{topilgan.length} ta topildi, boshqa: {boshqa}</p>
</div>
);
}
await chiz(<Royxat />);
console.log(matn("#soni"), "| filtrlashlar:", filtrlashlar);
await yoz("input", "Talaba-1");
console.log(matn("#soni"), "| filtrlashlar:", filtrlashlar);
500 ta topildi, boshqa: 0 | filtrlashlar: 1
111 ta topildi, boshqa: 0 | filtrlashlar: 2
Bu useMemo ning to'g'ri ishlatilishi: besh yuz elementni
filtrlash har chizishda takrorlanishi shart emas.
Haqiqiy sekinlik sabablari #
Ilova sekin bo'lsa, birinchi qadam - sababni topish, memo
sepish emas.
React DevTools da Profiler bo'limi bor: u har bir
komponent necha millisekund olganini ko'rsatadi. Sekin joy
odatda kutilgandan boshqacha bo'lib chiqadi.
Eng ko'p uchraydigan haqiqiy sabablar:
| Sabab | Yechim |
|---|---|
| Juda katta ro'yxat chizish | Virtuallashtirish |
| Har harfda so'rov yuborish | Kechiktirish (15-bo'lim) |
| Katta rasmlar | Hajmni kichraytirish |
| Ortiqcha kutubxonalar | Yig'ma hajmini tekshirish |
| Og'ir hisob chizishda | useMemo yoki server |
E'tibor bering: bu ro'yxatda "ortiqcha qayta chizish" oxirgi o'rinda. U ko'pincha aybdor emas.
React Compiler haqida #
React jamoasi React Compiler ustida ishlamoqda. U kodni
tahlil qilib, memo, useMemo va useCallback ni avtomatik
qo'yadi.
G'oya shunday: dasturchi optimallashtirish haqida umuman o'ylamasin, kompilyator buni o'zi qilsin.
U ishlashi uchun kod React qoidalariga amal qilishi kerak: props va holatni o'zgartirmaslik, chizish paytida yon ta'sir qilmaslik, ilgaklar qoidalarini buzmaslik.
Ya'ni bu darslikdagi qoidalarga amal qilsangiz, kompilyator kelganda kodingiz tayyor bo'ladi.
Hozircha uni ishlatish ixtiyoriy va u hali rivojlanmoqda.
- Ota qayta chizilganda bola ham chizilishini ko'ring.
- Bolani
memobilan o'rab, farqni ko'ring. - Bolaga obyekt prop berib,
memobuzilishini ko'ring. useMemobilan og'ir hisobni eslang.- Uni olib tashlab, hisoblashlar sonini solishtiring.
useCallbackbilan funksiyani barqarorlashtiring.useCallbacksizmemoishlamasligini isbotlang.- Oddiy qo'shishga
useMemoqo'yib, foydasizligini o'ylang. - Besh yuz elementli ro'yxatni filtrlang.
- React DevTools Profiler ni ishga tushiring.
Xulosa #
- Qayta chizish - qayta DOM emas: natija bir xil bo'lsa, DOM o'zgarmaydi.
memoprops o'zgarmasa qayta chizishni to'xtatadi.- Obyekt va funksiya props
memoni buzadi - ular har chizishda yangi. useMemonatijani,useCallbackfunksiyani eslaydi.- Erta optimallashtirish zarar keltiradi - ular ham ish talab qiladi.
- Avval o'lchang (Profiler), keyin tuzating.
- Ortiqcha qayta chizish sekinlikning eng keng tarqalgan sababi emas.
Keyingi bo'limda marshrutlashni o'rganamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.