17-bo‘lim

Unumdorlik

React qachon qayta chizadi, memo, useMemo va useCallback, ularni qachon ishlatmaslik va haqiqiy sekinlik sabablari.

🕑 11 daqiqa o‘qish 📄 794 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. React qachon qayta chizadi
  2. memo
  3. memo ni obyekt buzadi
  4. useMemo
  5. useCallback
  6. Ularni qachon ishlatmaslik kerak
  7. Ro'yxatni filtrlash
  8. Haqiqiy sekinlik sabablari
  9. React Compiler haqida
  10. Xulosa

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 #

JSX
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"));
Natija
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.

Qayta chizish - qayta DOM emas

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 #

JSX
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"));
Natija
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 #

JSX
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"));
Natija
chizildi: o'zgarmas
--- bosildi ---
chizildi: o'zgarmas

memo ishlamadi.

Har chizishda yangi obyekt yaratiladi

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:

JSX
<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 #

JSX
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);
Natija
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 #

JSX
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"));
Natija
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.

IlgakNimani eslaydi
useMemoHisoblash natijasini
useCallbackFunksiyaning o'zini

useCallback(f, []) - bu useMemo(() => f, []) ning qisqa shakli.

Ularni qachon ishlatmaslik kerak #

JSX
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} />));
Natija
<p>5</p>
<p>5</p>
Erta optimallashtirish zarar keltiradi

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:

  1. Sekinroq - qo'shimcha tekshiruv;
  2. Ko'proq xotira - har bir eslangan qiymat saqlanadi;
  3. 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:

  1. Hisoblash haqiqatan og'ir (minglab elementni saralash, filtrlash);
  2. Natija memo bilan o'ralgan komponentga prop sifatida ketadi;
  3. Qiymat useEffect ning bog'liqligi (10-bo'limdagi cheksiz sikl).

Ro'yxatni filtrlash #

JSX
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);
Natija
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 #

Avval o'lchang, keyin tuzating

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:

SababYechim
Juda katta ro'yxat chizishVirtuallashtirish
Har harfda so'rov yuborishKechiktirish (15-bo'lim)
Katta rasmlarHajmni kichraytirish
Ortiqcha kutubxonalarYig'ma hajmini tekshirish
Og'ir hisob chizishdauseMemo yoki server

E'tibor bering: bu ro'yxatda "ortiqcha qayta chizish" oxirgi o'rinda. U ko'pincha aybdor emas.

React Compiler haqida #

Kelajakda bu ilgaklar kerak bo'lmasligi mumkin

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.

Amaliy topshiriq
  1. Ota qayta chizilganda bola ham chizilishini ko'ring.
  2. Bolani memo bilan o'rab, farqni ko'ring.
  3. Bolaga obyekt prop berib, memo buzilishini ko'ring.
  4. useMemo bilan og'ir hisobni eslang.
  5. Uni olib tashlab, hisoblashlar sonini solishtiring.
  6. useCallback bilan funksiyani barqarorlashtiring.
  7. useCallback siz memo ishlamasligini isbotlang.
  8. Oddiy qo'shishga useMemo qo'yib, foydasizligini o'ylang.
  9. Besh yuz elementli ro'yxatni filtrlang.
  10. React DevTools Profiler ni ishga tushiring.

Xulosa #

  • Qayta chizish - qayta DOM emas: natija bir xil bo'lsa, DOM o'zgarmaydi.
  • memo props o'zgarmasa qayta chizishni to'xtatadi.
  • Obyekt va funksiya props memo ni buzadi - ular har chizishda yangi.
  • useMemo natijani, useCallback funksiyani 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.

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.