Concurrent Features — useTransition, useDeferredValue

~20 daq · React · Ilg'or mavzular

Muammodan boshlaylik

5000 ta mahsulotli katalog va qidiruv maydoni. Har harf yozilganda ro'yxat filtrlanadi:

function Katalog({ mahsulotlar }) {
  const [qidiruv, setQidiruv] = useState('');
  const natija = mahsulotlar.filter((m) =>
    m.nomi.toLowerCase().includes(qidiruv.toLowerCase())
  );

  return (
    <>
      <input value={qidiruv} onChange={(e) => setQidiruv(e.target.value)} />
      <MahsulotRoyxati mahsulotlar={natija} />    {/* og'ir render — 200 ms */}
    </>
  );
}

Foydalanuvchi "noutbuk" deb yozadi. Har bir harfdan keyin React ro'yxatni render qiladi — 200 ms. Shu vaqtda input ham qotadi: harflar kechikib, bo'laklab paydo bo'ladi. Yozish yoqimsiz.

Bu yerda ikki xil yangilanish bor:

  • Input qiymati — shoshilinch: foydalanuvchi bosgan tugma darhol ko'rinishi kerak
  • Ro'yxat — shoshilinch emas: 100–200 ms kechiksa ham hech kim sezmaydi

React 18 dan oldin barcha yangilanishlar teng edi: React render ni boshlasa, oxirigacha to'xtamasdan bajarardi. Concurrent rendering — React ga yangilanishlarning ustuvorligi ni aytish imkoniyati.


Concurrent rendering nima

Concurrent (parallel) rendering — React render ni to'xtatib turishi, muhimroq ishni bajarishi va keyin qaytishi mumkin:

Oddiy:          [──── ro'yxat render (200ms) ────][input]   ← input kutadi
                                                  
Concurrent:     [ro'yx][input][ro'yxat davomi...][input][ro'yxat...]
                         ↑ to'xtatildi, input birinchi

Shoshilinch bo'lmagan render fonda bajariladi va foydalanuvchi har yangi harf yozganda to'xtatiladi. Eskirgan render (eski qidiruv so'zi uchun) tashlab yuboriladi — uni tugatishning ma'nosi yo'q.

Siz React ga qaysi yangilanish shoshilinch emasligini ikki vosita bilan aytasiz: useTransition va useDeferredValue.


useDeferredValue — qiymatni kechiktirish

import { useState, useDeferredValue, memo } from 'react';

function Katalog({ mahsulotlar }) {
  const [qidiruv, setQidiruv] = useState('');
  const kechikkanQidiruv = useDeferredValue(qidiruv);

  const eskirgan = qidiruv !== kechikkanQidiruv;

  return (
    <>
      <input value={qidiruv} onChange={(e) => setQidiruv(e.target.value)} />
      <div style={{ opacity: eskirgan ? 0.6 : 1, transition: 'opacity 0.2s' }}>
        <MahsulotRoyxati mahsulotlar={mahsulotlar} qidiruv={kechikkanQidiruv} />
      </div>
    </>
  );
}

const MahsulotRoyxati = memo(function MahsulotRoyxati({ mahsulotlar, qidiruv }) {
  const natija = mahsulotlar.filter((m) =>
    m.nomi.toLowerCase().includes(qidiruv.toLowerCase())
  );
  return <ul>{natija.map((m) => <MahsulotQatori key={m.id} mahsulot={m} />)}</ul>;
});

Qanday ishlaydi:

  1. Harf yozildi → qidiruv = "nou", kechikkanQidiruv hali "no" (eski)
  2. React darhol render qiladi: input yangi qiymat bilan; MahsulotRoyxati — eski qiymat bilan → memo tufayli o'tkazib yuboriladi → input bir zumda yangilanadi
  3. Keyin fonda kechikkanQidiruv = "nou" bilan render boshlanadi
  4. Shu paytda yangi harf yozilsa — fondagi render tashlanadi, yangi qiymat bilan qayta boshlanadi

eskirgan — ko'rsatilayotgan ro'yxat hali eski qidiruvga tegishli; uni xiralashtirish foydalanuvchiga "yangilanyapti" deb ko'rsatadi.

[!WARNING] useDeferredValue faqat og'ir komponent memo bilan o'ralgan bo'lsa ishlaydi (68-dars). memo siz MahsulotRoyxati birinchi (shoshilinch) render da ham ota-ona bilan birga qayta render bo'ladi — va input yana qotadi. Hisob-kitobni ham memo li komponent ichiga qo'ying, ota-onada emas.


useTransition — yangilanishni "shoshilinch emas" deb belgilash

useDeferredValue — qiymatni olasiz va kechiktirasiz. useTransition — state yangilanishini o'zingiz belgilaysiz:

import { useState, useTransition } from 'react';

function Tablar() {
  const [tab, setTab] = useState('umumiy');
  const [isPending, startTransition] = useTransition();

  function tanla(yangi) {
    startTransition(() => {
      setTab(yangi);                     // shoshilinch emas
    });
  }

  return (
    <>
      <div role="tablist">
        <TabTugma faol={tab === 'umumiy'} onClick={() => tanla('umumiy')}>Umumiy</TabTugma>
        <TabTugma faol={tab === 'statistika'} onClick={() => tanla('statistika')}>
          Statistika
        </TabTugma>
      </div>

      <div style={{ opacity: isPending ? 0.6 : 1 }}>
        {tab === 'umumiy' ? <Umumiy /> : <Statistika />}   {/* Statistika — og'ir */}
      </div>
    </>
  );
}
  • startTransition(() => setX(...)) — ichidagi yangilanishlar transition: past ustuvorlik, to'xtatilishi mumkin
  • isPending — transition hali bajarilmoqda

Natija: "Statistika" bosildi → eski tab ko'rinib turadi (xira), og'ir tab fonda tayyorlanadi. Shu paytda foydalanuvchi fikrini o'zgartirib "Umumiy" ni bossa — interfeys darhol javob beradi, statistika render i tashlanadi.

Suspense bilan — eski kontentni saqlash

Transition ning yana bir muhim xususiyati — Suspense bilan:

// startTransition siz: yangi tab suspend bo'ladi → kontent skeleton ga almashadi (sakrash)
// startTransition bilan: React eski kontentni ko'rsatib turadi, yangisi tayyor bo'lguncha

Agar yangi kontent suspend bo'lsa (ma'lumot yoki lazy kod yuklanyapti — 81-dars), transition ichida React allaqachon ko'rinib turgan kontentni fallback ga almashtirmaydi — eski ekran isPending bilan qoladi. React Router va Next.js sahifa almashishlarini aynan shuning uchun transition ga o'raydi.

Async transition — React 19

const [isPending, startTransition] = useTransition();

function saqla() {
  startTransition(async () => {
    await api.saqla(forma);              // React 19 da async funksiya ham mumkin
    startTransition(() => {
      navigate('/royxat');
    });
  });
}

<button onClick={saqla} disabled={isPending}>
  {isPending ? 'Saqlanmoqda...' : 'Saqlash'}
</button>

React 19 da startTransition ga async funksiya berish mumkin — isPending butun async jarayon davomida true. Bu React 19 dagi "Actions" ning asosi.


Qaysi birini tanlash

useTransition useDeferredValue
Nimani boshqaradi State yangilanishini Qiymatni
Qachon setState ni o'zingiz chaqirasiz Qiymat props yoki boshqa hook dan keladi
Holat isPending qiymat !== kechikkan
Misol Tab almashtirish, sahifa navigatsiyasi, saralash tugmasi Qidiruv natijasi, filtr, grafik

[!INFO] Input ning o'z qiymatini transition ga o'ramang: startTransition(() => setQidiruv(e.target.value)) — input ning o'zi kechikadi va kursor sakrashi mumkin. Input — doim shoshilinch. Transition yoki deferred — faqat undan keyingi og'ir yangilanish uchun.


Debounce dan farqi

Qidiruv uchun debounce ham bor (25-dars). Farqi:

Debounce useDeferredValue
Kutish Qat'iy vaqt (masalan, 300 ms) Yo'q — kompyuter tez bo'lsa darhol
Tez qurilmada Baribir 300 ms kutadi Bir zumda
Sekin qurilmada 300 ms + og'ir render — baribir qotishi mumkin Render to'xtatiladi — input qotmaydi
Serverga so'rovlar ✅ Kamaytiradi ❌ Kamaytirmaydi
  • Serverga so'rov — debounce (yoki ikkalasi birga)
  • Brauzerdagi og'ir render — useDeferredValue

Nima uchun shunday?

Nima uchun React render ni to'xtatib turish imkoniyati juda muhim?

Chunki JavaScript bir oqimli: brauzer bir vaqtda faqat bitta ishni bajaradi. React 200 ms davom etadigan render ni boshlasa, shu 200 ms davomida brauzer klaviatura bosilishiga, sichqoncha harakatiga, animatsiyalarga javob bera olmaydi — sahifa "muzlaydi". Concurrent rendering bu muammoni ish ni mayda bo'laklarga bo'lib hal qiladi: React har bir kichik bo'lakdan keyin "foydalanuvchi biror narsa qildimi?" deb tekshiradi va kerak bo'lsa joyini bo'shatadi. Bu operatsion tizimlardagi vazifalarni rejalashtirishga o'xshaydi: musiqa pleer hech qachon katta faylni nusxalash tufayli to'xtamasligi kerak. Siz faqat React ga qaysi ish muhimroq ekanini aytasiz — qolganini rejalashtiruvchi o'zi hal qiladi.


Xulosa

Eslab qoling:

  • Concurrent rendering — shoshilinch bo'lmagan render to'xtatilishi va tashlanishi mumkin; input qotmaydi
  • Shoshilinch: input, bosish, hover. Shoshilinch emas: og'ir ro'yxat, tab kontenti, grafik
  • useDeferredValue(qiymat) — qiymatning kechiktirilgan nusxasi; og'ir komponent memo bilan bo'lishi shart
  • useTransition() → [isPending, startTransition]; startTransition(() => setX(...))
  • Transition + Suspense — eski kontent ko'rinib turadi, skeleton ga sakramaydi
  • React 19: startTransition(async () => ...)
  • Input ning o'z qiymatini transition ga o'ramang
  • Server so'rovlari — debounce; brauzerdagi og'ir render — deferred/transition

Keyingi dars

83-dars: Server Components — nima, Client Components dan farqi