IlmHamroh
JavaScript Full-stack/17-qism. React asoslari31/45-dars17 daqiqa
Mundarija (27)

useEffectEvent: effekt ichida eng so'nggi qiymat — qayta ulanmasdan

Qisqacha: useEffectEvent effekt ichidagi kodning bir qismini effekt hodisasiga ajratadi: u chaqirilgan paytdagi eng so'nggi props va state'ni ko'radi, lekin reaktiv emas — bog'liqliklar massiviga kirmaydi va o'zgarganda effektni qayta ishga tushirmaydi. Klassik holat: effekt stolga ulanadi ([table]), "ulandi" xabari esa joriy mavzuni (theme) ishlatadi. Mavzu o'zgarganda qayta ulanish kerak emas. React 19.2 (2025-yil oktabr) dan barqaror, vazifalar dagi 19.3.0 da bor. Qoidalar: faqat effekt ichidan chaqiriladi (chizish paytida emas, boshqa komponentga berilmaydi) va linterni jim qilish uchun ishlatilmaydi.

Bu darsda

  • Effekt ichida "reaktiv" va "reaktiv bo'lmagan" mantiqni ajrata olasiz.
  • useEffectEvent bilan keraksiz qayta ulanishni olib tashlaysiz va konsolda tekshirasiz.
  • Uning uchta qoidasini va buzilganda chiqadigan haqiqiy xatolarni bilasiz.
  • Qachon useEffectEvent kerak emasligini aytasiz.
  • vazifalar da online tinglovchisining 4 marta qayta ulanishini testda ko'rib, bir martaga tushirasiz.

Oldin bilishingiz kerak: Effekt hayot sikli, useEffect II: dependency massivi, Closure tuzoqlari, Sizga effekt kerak emas.

1. Nega bu kerak?

Effekt hayot sikli darsida ofitsiant planshetini stolga uladik: useEffect(() => connect(table), [table]). Stol almashsa — qayta ulanadi, to'g'ri. Endi Jasur aka yangi talab qo'ydi: "Ulanganda planshetda «ulandi» degan xabar chiqsin — tanlangan mavzu ranglarida (yorug' yoki qorong'i)."

Xabar theme ni ishlatadi. theme — props, ya'ni reaktiv qiymat. Massiv qoidasi bo'yicha u massivga kirishi shart: [table, theme]. Natija — ofitsiant mavzuni almashtirsa, planshet stoldan uzilib, qayta ulanadi. Sababi faqat xabar rangining o'zgarishi.

Agar theme ni massivdan olib tashlasak — massivni aldash: xabar eski mavzuda chiqadi. ESLint ogohlantiradi. Ikki yomon tanlov: ortiqcha qayta ulanish yoki eskirgan qiymat.

Hayotiy o'xshatish: siz do'stingiz bilan telefonda gaplashyapsiz. U sizdan "hozir soat necha?" deb so'raydi. Soatga qarash uchun qo'ng'iroqni uzib, qayta qo'ng'iroq qilmaysiz — shunchaki soatga qaraysiz. Qo'ng'iroq (ulanish) — do'stingizga bog'liq. Soat esa — har qaraganda eng yangi qiymat, lekin u qo'ng'iroqni qayta boshlamaydi. useEffectEvent — "soatga qarash".

2. Muammo jonli

2.1 [table, theme] — ortiqcha qayta ulanish

jsx
import { useEffect, useState } from "react";

function connect(table, onConnected) {
  console.log(`ulanish: stol ${table}`);
  onConnected();
  return () => console.log(`uzish: stol ${table}`);
}

function Waiter({ table, theme }) {
  useEffect(() => {
    const showNotice = () => {
      console.log(`xabar: ulandi (mavzu: ${theme})`);
    };
    return connect(table, showNotice);
  }, [table, theme]);
  return <p>Stol {table}, mavzu: {theme}</p>;
}

function App() {
  const [table, setTable] = useState(5);
  const [theme, setTheme] = useState("yorug'");
  return (
    <div>
      <Waiter table={table} theme={theme} />
      <button onClick={() => setTheme("qorong'i")}>
        Qorong'i
      </button>{" "}
      <button onClick={() => setTable(7)}>Stol 7</button>
    </div>
  );
}

Konsolda:

text
ulanish: stol 5
xabar: ulandi (mavzu: yorug')
uzish: stol 5
ulanish: stol 5
xabar: ulandi (mavzu: qorong'i)
uzish: stol 5
ulanish: stol 7
xabar: ulandi (mavzu: qorong'i)

O'rtadagi uch qator — muammo. «Qorong'i» bosildi: planshet stol 5 dan uzilib, o'sha stol 5 ga qayta ulandi. Haqiqiy ilovada bu — tarmoq so'rovi, server yuklamasi, bir soniyalik "ulanmoqda…" holati. Hammasi bitta rang uchun.

2.2 Effekt ichida ikki xil mantiq

Effektni sinchiklab o'qisak, ichida ikki xil kod bor:

Kod Nimaga bog'liq Qiymat o'zgarsa
connect(table, …) table qayta ulanish kerak
console.log(… theme …) theme faqat keyingi xabar yangi rangda bo'lsin

Birinchisi — reaktiv mantiq: u qiymatga "reaksiya" qilishi kerak. Ikkinchisi — reaktiv bo'lmagan mantiq: u eng so'nggi qiymatni o'qishi kerak, lekin unga reaksiya qilmasligi kerak. Muammo shundaki, effekt ikkalasini bitta qop ichiga solgan. Massiv esa butun qop uchun bitta.

Hodisa ishlovchisini eslang (Hodisalarni boshqarish): onClick ichidagi kod har doim eng so'nggi state'ni ko'radi va hech narsaga "reaksiya" qilmaydi — u faqat bosilganda ishlaydi. "Ulandi" xabari ham aslida hodisa: "ulanish sodir bo'ldi". Faqat bu hodisani foydalanuvchi emas, effekt chaqiradi.

flowchart LR
  P["props: table, theme"] --> E["useEffect, massiv: table"]
  P --> V["useEffectEvent: onConnected"]
  E -- "table o'zgarsa" --> C["uzish va qayta ulanish"]
  E -- "ulandi" --> V
  V -- "chaqirilganda" --> T["eng so'nggi theme bilan xabar"]

Diagrammada ikki yo'l bor. Yuqoridagisi — reaktiv: table o'zgarsa, effekt qayta ulanadi. Pastdagisi — reaktiv emas: effekt hodisasi faqat chaqirilganda theme ni o'qiydi va hech narsani qayta ishga tushirmaydi. Keyingi bo'limda shu ajratishni kodda qilamiz.

3. useEffectEvent

3.1 Sintaksis

Effekt hodisasi (Effect Event) — useEffectEvent bilan yaratilgan funksiya: effekt ichidan chaqiriladi, chaqirilgan paytdagi eng so'nggi props va state'ni ko'radi, o'zi esa reaktiv emas.

jsx
import { useEffect, useEffectEvent, useState } from "react";

function connect(table, onConnected) {
  console.log(`ulanish: stol ${table}`);
  onConnected();
  return () => console.log(`uzish: stol ${table}`);
}

function Waiter({ table, theme }) {
  // Effekt hodisasi: eng so'nggi theme, lekin reaktiv emas
  const onConnected = useEffectEvent(() => {
    console.log(`xabar: ulandi (mavzu: ${theme})`);
  });
  useEffect(() => connect(table, onConnected), [table]);
  return <p>Stol {table}, mavzu: {theme}</p>;
}

function App() {
  const [table, setTable] = useState(5);
  const [theme, setTheme] = useState("yorug'");
  return (
    <div>
      <Waiter table={table} theme={theme} />
      <button onClick={() => setTheme("qorong'i")}>
        Qorong'i
      </button>{" "}
      <button onClick={() => setTable(7)}>Stol 7</button>
    </div>
  );
}

Konsolda:

text
ulanish: stol 5
xabar: ulandi (mavzu: yorug')
uzish: stol 5
ulanish: stol 7
xabar: ulandi (mavzu: qorong'i)

Sakkiz qator o'rniga besh. «Qorong'i» bosilganda — hech narsa: qayta ulanish yo'q. «Stol 7» da — haqiqiy qayta ulanish va xabar yangi mavzu bilan. onConnected theme ning eng so'nggi qiymatini ko'rdi, garchi effekt theme haqida hech narsa bilmasa ham.

Uch narsaga e'tibor bering:

  1. useEffectEvent(fn) — hook, komponentning yuqori darajasida (hooklar qoidalari).
  2. Massiv — [table]. onConnected unga kirmaydi va ESLint buni so'ramaydi.
  3. onConnected effekt ichidan chaqirildi (connect orqali).
Render Commit Cleanup Effekt
ochilish: stol 5, yorug' <p> — ulanish 5, xabar (yorug')
«Qorong'i»: stol 5, qorong'i matn yangilandi — — (table o'sha)
«Stol 7»: stol 7, qorong'i matn yangilandi uzish 5 ulanish 7, xabar (qorong'i)

3.2 Nega "eng so'nggi" qiymat

Closure tuzoqlari darsida eskirgan closure'ni ko'rgan edik: funksiya yaratilgan paytdagi qiymatlarni "ushlab qoladi". Effekt [table] bilan faqat stol o'zgarganda qayta yaratiladi. Agar theme ni to'g'ridan-to'g'ri effekt ichida o'qisak, u effekt yaratilgan paytdagi theme bo'lardi.

useEffectEvent boshqacha ishlaydi. Har chizishda siz unga yangi funksiya berasiz (yangi theme bilan), React esa uni ichki "qutisiga" qo'yadi — commit paytida. Sizga qaytgan funksiya chaqirilganda, u o'z kodini emas, qutidagi eng oxirgi funksiyani ishga tushiradi. Shuning uchun effekt onConnected ning eski nusxasini ushlab qolsa ham, natija doim eng yangi qiymat bilan.

Yana bir detal: qaytgan funksiyaning o'zi har chizishda yangi (tekshirdik: prev === ev — false). Bu ataylab qilingan. Uni massivga yozsangiz, effekt har chizishda qayta ishlardi — shuning uchun qoida: effekt hodisasi massivga kirmaydi.

3.3 Versiya: barqaror, 19.2 dan

useEffectEvent uzoq vaqt eksperimental edi: experimental_useEffectEvent nomi bilan faqat canary versiyalarda bor edi. React 19.2 da (react.dev blogi, "React 19.2", 2025-10-01) u barqaror API sifatida chiqdi. O'sha relizda eslint-plugin-react-hooks ham uni taniydigan bo'ldi. Sayt oynasidagi React'da tekshiramiz:

jsx
import { useEffectEvent, version } from "react";

console.log("React", version, typeof useEffectEvent);

Konsolda:

text
React 19.3.0 function

vazifalar ham 19.3.0 da. Eski loyihalarda (React 18, 19.0, 19.1) u yo'q — u yerda pastdagi «latest ref» usuli uchraydi.

Tekshirib ko'ring: useEffectEvent ichida table ni ham o'qisak bo'ladimi? Masalan, xabar "stol 5 ga ulandi (yorug')" bo'lsin.

Javob

Bo'ladi: effekt hodisasi har qanday props va state'ni o'qiy oladi. Lekin table effektning o'zida reaktiv bo'lib qolishi kerak — massivda [table] turibdi, chunki ulanish unga bog'liq. Toza variant — table ni argument qilib berish: onConnected(table). Shunda hodisa aynan qaysi stol uchun chaqirilgani aniq ko'rinadi.

4. Qoidalar

4.1 Faqat effekt ichidan

Effekt hodisasini chizish paytida chaqirib bo'lmaydi. React buni ish vaqtida tekshiradi:

jsx
import { useEffectEvent, useState } from "react";

function App() {
  const [theme] = useState("yorug'");
  const onConnected = useEffectEvent(() => theme);
  return <p>Mavzu: {onConnected()}</p>; // ❌ chizish paytida
}

Konsolda:

text
Uncaught Error: A function wrapped in useEffectEvent can't be called during rendering.

"useEffectEvent ga o'ralgan funksiyani chizish paytida chaqirib bo'lmaydi." Chizish paytida kerakli qiymat — theme ning o'zi, unga o'ram kerak emas.

4.2 Massivga yozilmaydi, boshqa joyga berilmaydi

ESLint ikki xatoni ham ko'radi. Faylda effekt hodisasi massivga yozilgan va onClick dan chaqirilgan (plagin 7.1.1, qisqartirildi):

text
  10:14  warning  Functions returned from `useEffectEvent` must not be included in the dependency array. Remove `onConnected` from the list
  11:33  error    `onConnected` is a function created with React Hook "useEffectEvent", and can only be called from Effects and Effect Events in the same component
  • "useEffectEvent qaytargan funksiyalar bog'liqliklar massiviga kiritilmasligi kerak." U barqaror — massivda ma'nosi yo'q.
  • "onConnected — useEffectEvent bilan yaratilgan funksiya va faqat shu komponentdagi effektlar va effekt hodisalaridan chaqirilishi mumkin." Tugma uchun oddiy ishlovchi yozing. Boshqa komponentga props qilib berish ham taqiqlangan.

4.3 Linterni jim qilish uchun emas

Eng muhim qoida — yozilmagani. useEffectEvent ni "ESLint missing dependency deyapti — o'rab qo'yaman" deb ishlatish massivni aldashning yangi shakli. Savol bitta: bu qiymat o'zgarganda sinxronlash qayta boshlanishi kerakmi?

Qiymat Qayta boshlash kerakmi Qayerda
stol raqami (qaysi xonaga ulanish) effektda, massivda
xabar rangi, ovoz yoqilganmi effekt hodisasida
server manzili effektda
analytics'ga qo'shiladigan joriy savat soni effekt hodisasida

Agar javob "ha" bo'lsa-yu, siz qiymatni effekt hodisasiga yashirsangiz — stol almashganda planshet eski stolda qoladi. useEffectEvent faqat "bu — effektdan chaqiriladigan hodisa" bo'lganda to'g'ri.

5. Oldin qanday edi: "latest ref"

useEffectEvent dan oldin bu muammo ref bilan yechilardi. Ref — o'zgarganda qayta chizish chaqirmaydigan "quti", uni ikki darsdan keyin o'rganamiz (useRef I). G'oya: har chizishda eng so'nggi theme ni qutiga qo'yish, effektda esa qutidan o'qish. Eski kodda shunday parchani uchratasiz:

jsx
const themeRef = useRef(theme);
useEffect(() => {
  themeRef.current = theme; // har chizishdan keyin eng so'nggisi
});
useEffect(() => {
  return connect(table, () => show(themeRef.current));
}, [table]);

Bu ishlaydi, lekin uchta kamchiligi bor: qo'shimcha effekt va ref, kod niyatni yashiradi ("bu nima uchun?") va ESLint uni tekshira olmaydi. useEffectEvent — xuddi shu g'oyaning rasmiy, tekshiriladigan shakli. Yangi kodda — faqat u.

6. Vazifalar qadami: online bir marta ulanadi

6.1 Muammo — testda

useEffect II darsida kanondagi internet effektini ko'rgan edik: [vazifalar]. online hodisasida joriy ro'yxat bilan sinxronlash kerak, shuning uchun vazifalar massivda. Lekin bu degani — har vazifa belgilanganda online va offline tinglovchilari olib tashlanib, qayta ulanadi.

Yangi branch: refactor/effekt-hodisasi. Avval muammoni test bilan ushlaymiz. vi.spyOn(window, "addEventListener") — addEventListener chaqiruvlarini sanaydigan "josus" (Vitest: mock va spy — testlar qismida chuqur). Kanondagi test:

tsx
  test("online tinglovchisi bir marta ulanadi (ro'yxat o'zgarsa ham)", async () => {
    serverJavobi(ROYXAT);
    const ulash = vi.spyOn(window, "addEventListener");
    render(<App boshlangichRoyxat={ROYXAT} qoralama="" />);
    await waitFor(() => expect(matni("aloqa")).toBe(""));
    for (let i = 0; i < 3; i++) {
      fireEvent.click(
        screen.getByRole("checkbox", { name: "Non olish #bozor" }),
      );
    }
    const onlayn = ulash.mock.calls.filter(([tur]) => tur === "online");
    expect(onlayn).toHaveLength(1);
  });

Uch marta katak bosiladi, keyin "online" uchun addEventListener necha marta chaqirilgani sanaladi. Holat 28 kodida natija (haqiqiy):

text
     × online tinglovchisi bir marta ulanadi (ro'yxat o'zgarsa ham) 129ms
AssertionError: expected [ …(4) ] to have a length of 1 but got 4
      Tests  1 failed | 4 passed (5)

To'rt marta: ochilishda bir va har bosishda bir. Test yiqildi — endi tuzatamiz.

6.2 Tuzatish

sinxronla(vazifalar, …) chaqiruvi — reaktiv bo'lmagan mantiq: "internet qaytdi" — hodisa, u eng so'nggi ro'yxatni o'qishi kerak, lekin ro'yxat o'zgarganda tinglovchini qayta ulash kerak emas. Kanondagi diff:

diff
-import { useEffect, useState } from "react";
+import { useEffect, useEffectEvent, useState } from "react";
@@
+  const onlaynBoldi = useEffectEvent(() => {
+    void sinxronla(vazifalar, { setVazifalar, setTarmoq, setTahrirId });
+  });
+
+  // #28: internet — ochilganda bir marta ulanadi
   useEffect(() => {
     function oflayn() {
       sinxronniTox();
       setTarmoq("oflayn");
     }
-    function onlayn() {
-      void sinxronla(vazifalar, {
-        setVazifalar,
-        setTarmoq,
-        setTahrirId,
-      });
-    }
+    const onlayn = () => onlaynBoldi();
     addEventListener("offline", oflayn);
     addEventListener("online", onlayn);
     return () => {
       removeEventListener("offline", oflayn);
       removeEventListener("online", onlayn);
     };
-  }, [vazifalar]);
+  }, []);

E'tibor bering: addEventListener("online", onlaynBoldi) emas, const onlayn = () => onlaynBoldi(). Effekt hodisasini boshqa API'ga (bu yerda brauzerga) to'g'ridan-to'g'ri berish tavsiya etilmaydi — u faqat effekt kodi ichidan chaqirilsin. Kanondagi izoh (aynan):

tsx
  // #31: effekt hodisasi — ENG SO'NGGI vazifalarni o'qiydi, lekin
  // reaktiv emas: effektning bog'liqligi bo'lmaydi. #28 da effekt
  // [vazifalar] ga bog'liq edi — har o'zgarishda online tinglovchisi
  // olib tashlanib, qayta ulanardi (o'lchandi: 3 bosishda 4 marta)

Massiv [] — va bu safar aldash emas: effekt ichida reaktiv qiymat qolmadi (setTarmoq barqaror, onlaynBoldi — effekt hodisasi). ESLint jim.

6.3 Tekshiruvlar

text
      Tests  5 passed (5)
  • Commit: refactor: online tinglovchisi useEffectEvent bilan bir marta ulanadi.
  • npm test — node:test 147, Vitest 23 (+1 yangi test). Lint, format, tip — exit 0.
  • Brauzer tekshiruvi — 159/159 (oflayn → onlayn ssenariysi ham).
  • Diff: 2 fayl, +26 −12. Bundle deyarli o'zgarmadi: index-…js 248.41 → 248.45 kB.

Avval yiqiladigan test, keyin tuzatish — bu tartibni TDD (test-driven development) deyishadi. Bu yerda u isbot vazifasini ham bajardi: "4 marta" — taxmin emas, o'lchov.

7. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
mavzu/ovoz o'zgarganda qayta ulanish reaktiv bo'lmagan qiymat effekt massivida o'sha kodni useEffectEvent ga
can't be called during rendering effekt hodisasi JSX ichida chaqirilgan qiymatning o'zini ishlating
can only be called from Effects and Effect Events onClick dan yoki boshqa komponentdan oddiy ishlovchi yozing
must not be included in the dependency array effekt hodisasi massivda olib tashlang
stol almashdi, ulanish eski reaktiv qiymat effekt hodisasiga yashirilgan massivga qaytaring
useEffectEvent is not a function React 19.2 dan eski yangilang yoki "latest ref"

8. Mashqlar

Mashqlar kurs/mashqlar/17/31-useeffectevent/ da. Har yechimni «Ishga tushir» bilan sinang.

1-mashq (oson): reaktivmi?

Oshxona ekrani buyurtmalar kanaliga ulanadi. Effekt ichida to'rt qiymat o'qiladi. Har biri uchun javob bering: effekt massivida qolsinmi (massiv) yoki effekt hodisasiga o'tsinmi (hodisa)?

  • kitchenId — qaysi oshxona kanaliga ulanish:
  • soundOn — yangi buyurtmada ovoz chalinsinmi:
  • serverUrl — kanal manzili:
  • ordersCount — xabarga qo'shiladigan joriy buyurtmalar soni:
Yechim

kitchenId va serverUrl o'zgarsa — boshqa kanalga ulanish kerak: reaktiv, massivda. soundOn va ordersCount — faqat buyurtma kelganda o'qiladi; ular o'zgarganda qayta ulanish keraksiz: effekt hodisasida.

2-mashq (o'rta): bosishlar statistikasi

Sahifadagi har bosish window darajasida sanaladi va konsolga joriy savat soni bilan yoziladi. Hozir tinglovchi har count o'zgarishida qayta ulanyapti (ulandi qatorlari). useEffectEvent bilan tuzating: tinglovchi bir marta ulansin, lekin har bosishda eng so'nggi count yozilsin.

jsx
import { useEffect, useState } from "react";

function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log("ulandi");
    function handleClick() {
      console.log("bosish, savatda:", count);
    }
    window.addEventListener("click", handleClick);
    return () => window.removeEventListener("click", handleClick);
  }, [count]);
  return <button onClick={() => setCount(count + 1)}>Taom +</button>;
}

Konsolda:

text
ulandi
bosish, savatda: 0
ulandi
bosish, savatda: 1
ulandi
Yechim
jsx
import { useEffect, useEffectEvent, useState } from "react";

function App() {
  const [count, setCount] = useState(0);
  const onPageClick = useEffectEvent(() => {
    console.log("bosish, savatda:", count);
  });
  useEffect(() => {
    console.log("ulandi");
    const handleClick = () => onPageClick();
    window.addEventListener("click", handleClick);
    return () => window.removeEventListener("click", handleClick);
  }, []);
  return <button onClick={() => setCount(count + 1)}>Taom +</button>;
}

Konsolda:

text
ulandi
bosish, savatda: 0
bosish, savatda: 1

Bitta ulanish, qiymatlar — eng so'nggi (bosish paytida count hali oshmagan: window tinglovchisi tugmaning onClick idan keyin, lekin qayta chizishdan oldin ishlaydi). handleClick effekt ichidagi oddiy funksiya: u effekt hodisasini effekt kodi ichidan chaqiradi.

3-mashq (qiyin): ovozsiz rejim

Oshxona ekrani kitchen kanaliga ulanadi. Server xabari kelganda (blokda «Server: yangi buyurtma» tugmasi uni taqlid qiladi) ekran ovoz chaladi — agar muted o'chiq bo'lsa. Hozir «Ovozsiz» bosilganda kanal qayta ulanadi. Tuzating:

  • kanal faqat kitchen o'zgarganda qayta ulansin;
  • xabar kelganda eng so'nggi muted hisobga olinsin;
  • onMessage effekt hodisasi bo'lsin.

Ishora: subscribe(kitchen, onMessage) obunani bekor qiluvchi funksiya qaytaradi — «Sintaksis» bo'limidagi connect kabi. Kanalga xabarni modul darajasidagi emit() yuboradi.

jsx
import { useEffect, useState } from "react";

let listener = null; // "server" kanali — taqlid
function subscribe(kitchen, onMessage) {
  console.log("ulandi:", kitchen);
  listener = onMessage;
  return () => {
    console.log("uzildi:", kitchen);
    listener = null;
  };
}
const emit = (text) => listener?.(text);

function KitchenScreen({ kitchen, muted }) {
  useEffect(() => {
    return subscribe(kitchen, (text) => {
      console.log(muted ? `🔇 ${text}` : `🔔 ${text}`);
    });
  }, [kitchen, muted]);
  return <p>Oshxona: {kitchen}</p>;
}

function App() {
  const [muted, setMuted] = useState(false);
  return (
    <div>
      <KitchenScreen kitchen="asosiy" muted={muted} />
      <button onClick={() => setMuted(true)}>Ovozsiz</button>{" "}
      <button onClick={() => emit("osh × 2")}>
        Server: yangi buyurtma
      </button>
    </div>
  );
}

Konsolda:

text
ulandi: asosiy
uzildi: asosiy
ulandi: asosiy
🔇 osh × 2
Yechim
jsx
import { useEffect, useEffectEvent, useState } from "react";

let listener = null; // "server" kanali — taqlid
function subscribe(kitchen, onMessage) {
  console.log("ulandi:", kitchen);
  listener = onMessage;
  return () => {
    console.log("uzildi:", kitchen);
    listener = null;
  };
}
const emit = (text) => listener?.(text);

function KitchenScreen({ kitchen, muted }) {
  const onMessage = useEffectEvent((text) => {
    console.log(muted ? `🔇 ${text}` : `🔔 ${text}`);
  });
  useEffect(() => {
    return subscribe(kitchen, (text) => onMessage(text));
  }, [kitchen]);
  return <p>Oshxona: {kitchen}</p>;
}

function App() {
  const [muted, setMuted] = useState(false);
  return (
    <div>
      <KitchenScreen kitchen="asosiy" muted={muted} />
      <button onClick={() => setMuted(true)}>Ovozsiz</button>{" "}
      <button onClick={() => emit("osh × 2")}>
        Server: yangi buyurtma
      </button>
    </div>
  );
}

Konsolda:

text
ulandi: asosiy
🔇 osh × 2

Kanal bir marta ulandi, «Ovozsiz» uni uzmadi, xabar esa eng so'nggi muted bilan () chiqdi. Effekt hodisasi argument ham oladi (text) — u oddiy funksiya kabi chaqiriladi. Kanalga onMessage ning o'zi emas, uni chaqiradigan arrow funksiya berildi: effekt hodisasi effekt kodidan tashqariga chiqmadi.

4-mashq: Vazifalar qadami

vazifalar da refactor/effekt-hodisasi branch'ini oching. Avval «Muammo — testda» bo'limidagi testni tekshiruv/App.test.tsx ga qo'shib, uning yiqilishini ko'ring (expected … to have a length of 1 but got 4). Keyin «Tuzatish» dagi o'zgarishni kiriting va test o'tsin.

Yechim

Tekshiruv ro'yxati:

  • npx vitest run tekshiruv/App.test.tsx — avval Tests 1 failed | 4 passed (5), keyin Tests 5 passed (5).
  • Internet effektining massivi — []; grep -n "useEffectEvent" src/App.tsx — import va bitta chaqiruv.
  • npm run lint — jim: onlaynBoldi massivda yo'q va faqat effekt ichidan chaqiriladi.
  • Brauzerda: DevTools → Network → "Offline" ni yoqing va o'chiring — ro'yxat server bilan sinxronlanadi.

Commit — refactor: online tinglovchisi useEffectEvent bilan bir marta ulanadi. PR'ni birlashtiring.

9. Real ishda

  • Chat, xarita, real-time panellar — ulanish bir narsaga (xona, foydalanuvchi), xabar ko'rinishi esa boshqa narsalarga (mavzu, ovoz, til) bog'liq bo'lgan har joyda useEffectEvent kerak bo'ladi.
  • Custom hooklar ichida u ayniqsa foydali: hook tashqaridan ishlovchi (onMessage) qabul qiladi va har chizishda yangi funksiya kelsa ham qayta ulanmaydi (Custom hooklar I). vazifalar dagi useOynaHodisasi hooki shunday quriladi.
  • Eski loyihalarda "latest ref" naqshi va useEvent nomli o'z hooklari uchraydi — React 19.2 ga o'tganda ular useEffectEvent bilan almashtiriladi.
  • Intervyu savollari: "useEffectEvent qanday muammoni hal qiladi?", "uni qayerdan chaqirib bo'lmaydi?", "nega uni massivga yozmaymiz?".

Xulosa

  • Effekt ichida ikki xil mantiq bor: reaktiv (o'zgarsa — qayta sinxronlash) va reaktiv bo'lmagan (faqat eng so'nggi qiymatni o'qish).
  • useEffectEvent(fn) reaktiv bo'lmagan qismni ajratadi: eng so'nggi props/state'ni ko'radi, massivga kirmaydi.
  • Qoidalar: faqat effekt va effekt hodisalari ichidan; chizish paytida emas (can't be called during rendering); boshqa komponentga berilmaydi.
  • Linterni jim qilish uchun emas: "qiymat o'zgarsa qayta ulanish kerakmi?" — ha bo'lsa, massivda qolsin.
  • React 19.2 (2025-10-01) dan barqaror; vazifalar da online tinglovchisi 4 martadan 1 martaga tushdi (test bilan isbotlandi).

Keyingi dars: Effektda ma'lumot olish va nega bu ko'pincha noto'g'ri — fetch + useEffect ning to'liq shakli: yuklanish, xato, poyga va nega katta ilovalarda kutubxona yoki framework tanlanadi.

Manbalar

  • React: useEffectEvent — react.dev/reference/react/useEffectEvent
  • React: "Separating Events from Effects" — react.dev/learn/separating-events-from-effects
  • React blogi: "React 19.2" (2025-10-01) — useEffectEvent barqaror API sifatida — react.dev/blog/2025/10/01/react-19-2
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
useEffectEvent: effekt ichida eng so'nggi qiymat — qayta ulanmasdan — IlmHamroh