IlmHamroh
JavaScript Full-stack/17-qism. React asoslari29/45-dars18 daqiqa
Mundarija (29)

Effekt hayot sikli va StrictMode: nega useEffect dev'da ikki marta ishlaydi

Qisqacha: Effektni komponentning "tug'ilishi va o'limi" (mount/unmount) bilan emas, sinxronlashni boshlash va to'xtatish sikli bilan o'ylang. Komponent ekranda turgan vaqt ichida effekt bir necha marta boshlanib, to'xtashi mumkin — har safar bog'liqligi o'zgarganda. StrictMode dev rejimda buni ataylab sinaydi: har effekt uchun setup → cleanup → setup qiladi. Cleanup to'g'ri bo'lsa, foydalanuvchi farqni sezmaydi. Sezsa — cleanup'da xato bor, va u production'da ham bor. Production'da takror yo'q. StrictMode'ni o'chirish yoki "bir marta ishladimi" bayrog'i — davo emas.

Bu darsda

  • Effektni "mount/unmount" emas, "boshla — to'xtat" deb o'ylay olasiz va farqini misolda ko'rsatasiz.
  • StrictMode dev'da effekt bilan nima qilishini konsolda kuzatasiz.
  • Ikki marta ishlaganda buziladigan effektni topib, cleanup bilan tuzatasiz.
  • Reaktiv qiymat nima ekanini va qaysilari effektni qayta ishga tushirishini aytasiz.
  • Analytics, xarid va ilovani sozlash kabi "bir martalik" ishlar uchun to'g'ri joyni tanlaysiz.

Oldin bilishingiz kerak: useEffect III: cleanup, useEffect II: dependency massivi, Birinchi React loyiha (StrictMode).

1. Nega bu kerak?

Birinchi React loyiha darsida StrictMode komponentni dev rejimda ikki marta chaqirishini o'lchagan edik va "effektlarni ham ikki marta ishga tushiradi" deb va'da bergan edik. Oldingi useEffect III darsida buni vazifalar ning tarmoq jurnalida ko'rdik: bitta ochilishda ikki GET, birinchisi bekor qilingan.

Sardorning birinchi reaksiyasi tushunarli edi: "Ikki marta so'rov — server yuklanadi! Ikki marta analytics — statistika buziladi!" U ikki "tuzatish" o'yladi: main.tsx dan <StrictMode> ni olib tashlash yoki effekt ichiga "allaqachon ishladimi?" bayrog'ini qo'yish. Ikkalasi ham noto'g'ri. Bugun nega ekanini va StrictMode aslida qanday sovg'a ekanini ko'ramiz.

Buning uchun avval effekt haqidagi bitta keng tarqalgan mifni buzish kerak. Hayotiy o'xshatish: ofitsiantning planshet ulanishi. Ofitsiant smenaga kelganda planshet stolga ulanadi — bu "komponent tug'ildi" emas. Ofitsiant boshqa stolga o'tsa, planshet eski stoldan uziladi va yangisiga ulanadi. Smena davomida bu o'nlab marta bo'ladi. Ulanish — komponentning hayoti emas, hozir qaysi stolga xizmat qilayotganining aksi.

2. Mif: "effekt — mount va unmount"

2.1 Eski fikrlash

React'da hooklardan oldin class komponentlar bor edi (Class komponentlarni o'qish darsida eski kodni o'qishni o'rganamiz). Ularda "hayot sikli metodlari" bor edi: componentDidMount (komponent paydo bo'ldi), componentDidUpdate (yangilandi), componentWillUnmount (yo'qolmoqda). Ko'p maqola va eski kurslarda shunday jadval uchraydi: "useEffect(fn, []) — bu componentDidMount, cleanup esa — componentWillUnmount".

Bu jadval yarim haqiqat va xavfli. U sizni "komponent hayotining qaysi nuqtasida nima qilay?" deb o'ylashga majbur qiladi. Natijada [] ga hamma narsani tiqib, massivni aldash boshlanadi (useEffect II).

Mount (mount) — komponent birinchi marta ekranga qo'yilishi; unmount (unmount) — ekrandan butunlay olib tashlanishi. Bu so'zlar hali ham foydali, lekin effekt uchun emas, komponent uchun.

2.2 Effektning nuqtai nazari

React hujjati effektni boshqacha o'ylashni so'raydi. Effekt — bitta sinxronlash jarayoni: "stol 5 ga ulangan bo'l". Uning faqat ikki harakati bor: boshlash (setup) va to'xtatish (cleanup). Effektning o'zi komponent qachon tug'ilganini bilmaydi va bilishi shart emas. U bitta savolga javob beradi: "hozirgi props va state bilan nimaga ulanishim kerak?"

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

function connect(table) {
  console.log(`ulandi: stol ${table}`);
  return () => console.log(`uzildi: stol ${table}`);
}

function Waiter({ table }) {
  useEffect(() => {
    const disconnect = connect(table); // boshlash
    return disconnect; // to'xtatish
  }, [table]);
  return <p>Xizmatda: stol {table}</p>;
}

function App() {
  const [table, setTable] = useState(5);
  const [onShift, setOnShift] = useState(true);
  return (
    <div>
      {onShift && <Waiter table={table} />}
      <button onClick={() => setTable(7)}>Stol 7</button>{" "}
      <button onClick={() => setOnShift(false)}>Yopish</button>
    </div>
  );
}

Konsolda:

text
ulandi: stol 5
uzildi: stol 5
ulandi: stol 7
uzildi: stol 7

Ikki nuqtai nazardan bir xil voqealar:

Voqea Komponent nuqtai nazari Effekt nuqtai nazari
ochilish Waiter mount bo'ldi stol 5 bilan sinxronlash boshlandi
«Stol 7» Waiter yangilandi (props) stol 5 to'xtadi, stol 7 boshlandi
«Yopish» Waiter unmount bo'ldi stol 7 to'xtadi

Komponentning bitta hayoti — effektning ikkita sikli. Agar "faqat mount'da ulanaman" deb [] yozganingizda, «Stol 7» dan keyin planshet hamon stol 5 ga ulangan bo'lardi. Effekt nuqtai nazaridan o'ylaganingizda esa massiv o'z-o'zidan to'g'ri chiqadi: "men table bilan sinxronlayman" → [table].

stateDiagram-v2
  [*] --> Ulangan5: setup (stol 5)
  Ulangan5 --> Uzilgan: cleanup (stol 5)
  Uzilgan --> Ulangan7: setup (stol 7)
  Ulangan7 --> [*]: cleanup (stol 7)

Diagrammaga qarang: unda "mount" ham, "update" ham yo'q — faqat ikki holat (ulangan, uzilgan) va ikki o'tish (setup, cleanup). Har qanday ketma-ketlik shu ikki o'tishdan yig'iladi.

Tekshirib ko'ring: Komponent ekranda turganda effekt uch marta boshlanib, ikki marta to'xtadi. Komponent hali ham ekranda. Effekt necha marta to'xtashi kerak va qachon?

Javob

Yana bir marta — komponent olib tashlanganda yoki bog'liqlik keyingi safar o'zgarganda. Har "boshlash" ning o'z "to'xtatish" i bor: uch setup — oxir-oqibat uch cleanup. Hozir ochiq bitta sinxronlash qolgan.

3. Reaktiv qiymatlar

3.1 Effekt nimaga "reaksiya" qiladi

useEffect II darsida reaktiv qiymat atamasini kiritdik: chizishdan chizishga o'zgarishi mumkin bo'lgan qiymat. Endi uni effekt nuqtai nazaridan ko'ramiz. Effekt ichida o'qilgan har reaktiv qiymat — sinxronlashning bir qismi. U o'zgarsa, eski sinxronlash noto'g'ri bo'lib qoladi va qayta boshlash kerak.

Qiymat Reaktivmi Nega
props (table) ota boshqa qiymat berishi mumkin
state (category) set… bilan o'zgaradi
komponent ichida ulardan hisoblangan (const url = …table…) manbasi reaktiv
komponentdan tashqaridagi const SERVER = "…" hech qachon o'zgarmaydi
set… funksiyalari React barqaror beradi
useRef obyekti (useRef I) barqaror quti

3.2 Hisoblangan qiymat ham reaktiv

Ko'p qoqiladigan joy — uchinchi qator. Sardor manzilni effektdan oldin yasadi:

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

const SERVER = "https://ilmhamroh.uz/api/mashq"; // reaktiv emas

function App() {
  const [category, setCategory] = useState("taom");
  const url = `${SERVER}/menyu?turkum=${category}`; // reaktiv!
  useEffect(() => {
    console.log("sinxron:", url);
  }, [url]);
  return (
    <button onClick={() => setCategory("ichimlik")}>Ichimlik</button>
  );
}

Konsolda:

text
sinxron: https://ilmhamroh.uz/api/mashq/menyu?turkum=taom
sinxron: https://ilmhamroh.uz/api/mashq/menyu?turkum=ichimlik

url — satr, category dan hisoblangan. U ham reaktiv, shuning uchun massivda. SERVER esa massivda yo'q — u komponentdan tashqarida va ESLint uni so'ramaydi. Agar SERVER ni komponent ichiga ko'chirsangiz, ESLint uni massivga qo'shishni so'raydi. U har safar bir xil satr bo'lgani uchun effekt baribir ortiqcha ishlamaydi. Lekin ma'nosi o'zgaradi: endi u "komponentga tegishli" qiymat.

4. StrictMode: dev rejimdagi sinov

4.1 Nima qiladi

StrictMode dev rejimda har komponentni ekranga qo'ygandan keyin bir marta ataylab "olib tashlab, qaytadan qo'yadi". Faqat effektlar va ref'lar uchun: state saqlanadi, DOM qayta yaratilmaydi. Natijada har effekt: setup → cleanup → setup. Bu — «Effektning nuqtai nazari» bo'limidagi g'oyaning avtomatik sinovi: "effektingiz har qanday boshla-to'xtat ketma-ketligiga bardosh beradimi?"

Ofitsiant misolini StrictMode bilan ishga tushiramiz. Bu safar ochiq ulanishlar sonini ham sanaymiz:

jsx
import { StrictMode, useEffect } from "react";
import { createRoot } from "react-dom/client";

let open = 0; // ochiq ulanishlar (server tomonida)

function connect(table) {
  open++;
  console.log(`ulandi: stol ${table} (ochiq: ${open})`);
  return () => {
    open--;
    console.log(`uzildi: stol ${table} (ochiq: ${open})`);
  };
}

function Waiter({ table }) {
  console.log("render");
  useEffect(() => connect(table), [table]);
  return <p>Xizmatda: stol {table}</p>;
}

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <Waiter table={5} />
  </StrictMode>,
);

Konsolda:

text
render
render
ulandi: stol 5 (ochiq: 1)
uzildi: stol 5 (ochiq: 0)
ulandi: stol 5 (ochiq: 1)

Ikki narsa ikki marta: render (komponent funksiyasi — Birinchi React loyiha darsidagi o'lchov) va effekt (setup → cleanup → setup). Oxirgi qatorga qarang: ochiq: 1. Foydalanuvchi uchun natija bitta ulanish bilan bir xil. useEffect(() => connect(table), [table]) — connect qaytargan funksiya to'g'ridan-to'g'ri cleanup bo'ldi.

Render Commit Effekt Cleanup
1-qadam: Waiter × 2 (dev) <p> DOM'ga setup → ochiq 1 —
2-qadam (sinov): — — — cleanup → ochiq 0
3-qadam (sinov): — — setup → ochiq 1 —

4.2 Cleanup bo'lmasa — xato ko'rinadi

Endi cleanup'ni "unutamiz":

jsx
import { StrictMode, useEffect } from "react";
import { createRoot } from "react-dom/client";

let open = 0;

function connect(table) {
  open++;
  console.log(`ulandi: stol ${table} (ochiq: ${open})`);
  return () => {
    open--;
  };
}

function Waiter({ table }) {
  useEffect(() => {
    connect(table); // ❌ uzish funksiyasi tashlab yuborildi
  }, [table]);
  return <p>Xizmatda: stol {table}</p>;
}

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <Waiter table={5} />
  </StrictMode>,
);

Konsolda:

text
ulandi: stol 5 (ochiq: 1)
ulandi: stol 5 (ochiq: 2)

Bitta ofitsiant — ikkita ochiq ulanish. Bu StrictMode'ning "xatosi" emas: u production'dagi kelajakdagi xatoni oldindan ko'rsatdi. StrictMode'siz bu kod birinchi ochilishda to'g'ri ko'rinardi. Lekin table har o'zgarganda yangi ulanish qo'shilib, eskisi ochiq qolardi — o'sha oqish. StrictMode uni birinchi sekundda, sizning kompyuteringizda, dev rejimda ko'rsatdi.

Maslahat: Qoida juda sodda: dev'da effekt ikki marta ishlaganda foydalanuvchi farqni sezsa — cleanup'ni tuzating. Sezmasa — hammasi joyida, ikkinchi ishga tushishga e'tibor bermang.

4.3 Yangilanishda takror yo'q

StrictMode sinovi faqat birinchi marta ekranga qo'yilganda bo'ladi. Keyingi chizishlarda effekt odatdagidek ishlaydi:

jsx
import { StrictMode, useEffect, useState } from "react";
import { createRoot } from "react-dom/client";

function App() {
  const [n, setN] = useState(0);
  useEffect(() => {
    console.log("setup", n);
    return () => console.log("cleanup", n);
  }, [n]);
  return <button onClick={() => setN(n + 1)}>+</button>;
}

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

Konsolda:

text
setup 0
cleanup 0
setup 0
cleanup 0
setup 1

Dastlabki uch qator — StrictMode sinovi. Keyingi ikkitasi — oddiy "bog'liqlik o'zgardi": eski cleanup, yangi setup.

4.4 Production'da — yo'q

StrictMode faqat dev'da ishlaydi: npm run build dan keyin u hech narsa qilmaydi. Buni Birinchi React loyiha darsida o'lchagan edik (dev — 4 qator, preview — 2 qator). vazifalar da ham xuddi shunday: npm run dev da ikki GET (birinchisi bekor qilinadi), build qilingan dist/ da — bitta.

Saytdagi «Ishga tushir» oynasida ham React'ning dev versiyasi ishlaydi. Shuning uchun <StrictMode> bilan yozilgan bloklarda takrorni o'z ko'zingiz bilan ko'rasiz.

5. "Bir martalik" ishlar

5.1 Analytics: ikki marta — normal

«Bahor» Jasur akaga "menyu necha marta ochildi" statistikasini ko'rsatadi. Har ochilishda serverga menyu_ochildi hodisasi yuboriladi:

jsx
import { StrictMode, useEffect } from "react";
import { createRoot } from "react-dom/client";

function sendEvent(name) {
  console.log("analytics:", name); // haqiqiy ilovada — serverga
}

function Menu() {
  useEffect(() => {
    sendEvent("menyu_ochildi");
  }, []);
  return <h1>Menyu</h1>;
}

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <Menu />
  </StrictMode>,
);

Konsolda:

text
analytics: menyu_ochildi
analytics: menyu_ochildi

Dev'da ikki marta. Bu muammomi? React hujjati aniq javob beradi: yo'q. Production'da bir marta yuboriladi. Dev statistikasi esa baribir haqiqiy foydalanuvchilarniki bilan aralashmasligi kerak — dev'da hodisalarni umuman yubormaslik yoki alohida "dev" loyihaga yuborish odatiy amaliyot. Bu yerda cleanup kerak emas: "yuborildi" ni qaytarib bo'lmaydi va bunga hojat ham yo'q.

5.2 Xarid: effekt emas, hodisa

Boshqa holat: "Buyurtma berish" — serverga POST. Sardor uni effektga qo'ydi: "buyurtma sahifasi ochilsa — yubor". Dev'da ikki buyurtma! Lekin bu yerda StrictMode haqiqiy xatoni ko'rsatdi: buyurtma sahifa ochilgani uchun emas, mehmon tugmani bosgani uchun yuborilishi kerak. Sahifa qayta ochilsa (Orqaga, yangilash), effekt yana buyurtma yuborardi — production'da ham.

Qoida: foydalanuvchi harakatiga javob — hodisa ishlovchisida (Hodisalarni boshqarish). Effekt — faqat "ekranda shu turibdi, tashqi tizim mos bo'lsin". Bu farq Sizga effekt kerak emas darsining asosi.

Tekshirib ko'ring: Uch ish bor: (a) menyu ochilganda analytics'ga "ko'rildi" yuborish; (b) «Buyurtma berish» bosilganda serverga POST; (c) sahifa ochilganda chat serveriga ulanish. Qaysi biri effektda, qaysi biri hodisa ishlovchisida bo'lishi kerak? Qaysi biriga cleanup kerak?

Javob

(a) — effekt, cleanup shart emas: dev'dagi takror zararsiz, production'da bir marta. (b) — hodisa ishlovchisi: sabab — mehmonning bosishi, sahifaning ochilishi emas. (c) — effekt va cleanup (uzish) albatta: busiz StrictMode ikkita ulanishni ko'rsatadi, production'da esa sahifa almashganda ulanishlar to'planadi.

5.3 Ilovani bir marta sozlash

Ba'zi ishlar butun ilova uchun bir marta bo'ladi: kutubxonani ishga tushirish, tokenni tekshirish. Ular komponentga emas, modulga tegishli. Ikki yo'l:

jsx
import { StrictMode, useEffect } from "react";
import { createRoot } from "react-dom/client";

let didInit = false; // modul darajasida — komponentdan tashqarida

function App() {
  useEffect(() => {
    if (didInit) return;
    didInit = true;
    console.log("ilova sozlandi"); // bir marta, dev'da ham
  }, []);
  return <p>Bahor</p>;
}

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

Konsolda:

text
ilova sozlandi

Yoki undan ham soddaroq — kodni umuman komponentdan tashqariga, main.tsx ga (createRoot dan oldin) yozish. Bu yo'lni vazifalar tanlagan: tema paneli, Service Worker ro'yxatdan o'tishi va Web Vitals — main.tsx da, App dan tashqarida (useEffect I dagi kanon main.tsx ni eslang).

Diqqat: Komponent ichida "allaqachon ishladimi?" bayrog'ini qo'yish (ko'pincha keyingi darsdagi useRef bilan) — StrictMode'ni "aldash". U takrorni yashiradi, lekin cleanup'dagi xatoni tuzatmaydi. Bundan tashqari, table o'zgarganda ham effekt endi qayta ulanmaydi. Modul darajasidagi didInit dan farqli — u butun ilova uchun bitta bo'lgani uchun to'g'ri.

5.4 Ma'lumot olish: ikki so'rov

Effektda fetch dev'da ikki marta yuboriladi. useEffect III dagi AbortController cleanup'i bilan birinchisi bekor qilinadi — natija bitta. vazifalar ning haqiqiy dev jurnali:

text
  BEKOR GET /api/mashq/vazifalar/de96… — net::ERR_ABORTED      ← 1-effekt, cleanup bekor qildi
  200 GET /api/mashq/vazifalar/de96…
  200 PUT /api/mashq/vazifalar/de96…                         ← server kalitni bilmadi — ro'yxat yuborildi

Agar cleanup bo'lmaganda, ikkala GET ham javob qaytarardi va ikkalasi ham "server kalitni bilmaydi" deb ikki PUT yuborardi. StrictMode buni darhol ko'rsatgan bo'lardi. Ikki so'rov server uchun qimmat bo'lsa — bu effektda ma'lumot olishning o'z kamchiligi. Bu haqda Effektda ma'lumot olish darsida gaplashamiz.

6. StrictMode yana nimani tekshiradi

To'liq ro'yxat — StrictMode chuqur darsida. Qisqacha:

Tekshiruv Qaysi xatoni ochadi
komponent funksiyasi ikki marta chizish paytidagi yon ta'sir (toza emas)
effekt: setup → cleanup → setup cleanup yo'q yoki noto'liq
ref callback: ikki marta (React 19) ref cleanup yo'q (useRef II)
eskirgan API'lar ogohlantirish

Uchinchi qator haqida bir og'iz: DOM elementiga funksiya bilan ulanish ham setup → cleanup → setup sinovidan o'tadi. Bu React 19 da qo'shildi va useRef II darsida ko'ramiz.

7. Vazifalar kuzatuvi

Bu dars vazifalar kodini o'zgartirmaydi — useEffect III dagi commit allaqachon StrictMode sinovidan o'tadi. O'rniga kanondagi oltita effektni shu darsning savoli bilan tekshiramiz: "setup → cleanup → setup — foydalanuvchi farq sezadimi?"

Effekt Ikki marta ishlasa Sezadimi
localStorage (saqla) bir xil ro'yxat ikki marta yoziladi yo'q
boshqa tablar (tingla) obuna → bekor → obuna yo'q (bitta tinglovchi)
server (sinxronla) GET → bekor → GET yo'q (bitta javob)
internet, URL, pagehide tinglovchi → olib tashlash → tinglovchi yo'q

Hammasi "yo'q" — demak kanondagi effektlar to'g'ri yozilgan. main.tsx dagi <StrictMode> saqlanadi.

8. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
dev'da hamma narsa ikki marta StrictMode bu xato emas — foydalanuvchi farq sezadimi, tekshiring
dev'da ikkita ulanish, ikkita tinglovchi cleanup yo'q cleanup qaytaring
dev'da ikki buyurtma (POST) harakat effektda hodisa ishlovchisiga ko'chiring
<StrictMode> o'chirildi, "tuzaldi" xato yashirildi, qolgan qaytaring, cleanup'ni tuzating
"bir marta ishladimi" bayrog'i komponentda StrictMode aldandi modul darajasi yoki to'g'ri cleanup
[] — "mount'da bir marta" deb massiv aldangan effekt nuqtai nazaridan o'ylang

9. Mashqlar

Mashqlar kurs/mashqlar/17/29-effekt-hayot-sikli/ da. Har yechimni «Ishga tushir» bilan sinang.

1-mashq (oson): StrictMode konsoli

Kodni ishga tushirmasdan, konsolni oldindan yozing (StrictMode yoqilgan).

jsx
import { StrictMode, useEffect } from "react";
import { createRoot } from "react-dom/client";

function Kitchen() {
  useEffect(() => {
    console.log("pech yoqildi");
    return () => console.log("pech o'chdi");
  }, []);
  return <p>Oshxona</p>;
}

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <Kitchen />
  </StrictMode>,
);
  • Konsolda nechta qator:
  • Oxirgi qator:
Yechim

Konsolda:

text
pech yoqildi
pech o'chdi
pech yoqildi

StrictMode sinovi: setup → cleanup → setup. Oxirida pech yoqilgan — foydalanuvchi uchun bitta "yoqish" bilan bir xil.

2-mashq (o'rta): ikki ulanishni tuzating

Mehmonlar soni hisoblagichi subscribe orqali "server" ga obuna bo'ladi. StrictMode'da obunalar soni 2 bo'lib qolyapti. Effektni tuzating: oxirida obunalar: 1 bo'lsin. Ishora: subscribe obunani bekor qiluvchi funksiya qaytaradi — «StrictMode: dev rejimdagi sinov» bo'limidagi connect kabi.

jsx
import { StrictMode, useEffect } from "react";
import { createRoot } from "react-dom/client";

let subscribers = 0;

function subscribe(onGuests) {
  subscribers++;
  console.log("obunalar:", subscribers);
  onGuests(12);
  return () => {
    subscribers--;
    console.log("obunalar:", subscribers);
  };
}

function GuestCounter() {
  useEffect(() => {
    subscribe((n) => console.log("mehmonlar:", n));
  }, []);
  return <p>Mehmonlar hisoblagichi</p>;
}

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <GuestCounter />
  </StrictMode>,
);

Konsolda:

text
obunalar: 1
mehmonlar: 12
obunalar: 2
mehmonlar: 12
Yechim
jsx
import { StrictMode, useEffect } from "react";
import { createRoot } from "react-dom/client";

let subscribers = 0;

function subscribe(onGuests) {
  subscribers++;
  console.log("obunalar:", subscribers);
  onGuests(12);
  return () => {
    subscribers--;
    console.log("obunalar:", subscribers);
  };
}

function GuestCounter() {
  useEffect(() => {
    const showGuests = (n) => console.log("mehmonlar:", n);
    const unsubscribe = subscribe(showGuests);
    return unsubscribe;
  }, []);
  return <p>Mehmonlar hisoblagichi</p>;
}

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <GuestCounter />
  </StrictMode>,
);

Konsolda:

text
obunalar: 1
mehmonlar: 12
obunalar: 0
obunalar: 1
mehmonlar: 12

Oxirgi holat — bitta obuna. subscribe qaytargan funksiyani tashlab yubormaslik — butun tuzatish. Qisqa shakl ham bor: useEffect(() => subscribe(…), []) — arrow funksiya subscribe natijasini o'zi qaytaradi.

3-mashq (qiyin): stolni almashtirish

Waiter komponenti hozir faqat birinchi stolga ulanadi: massiv []. «Stol 7» bosilganda ham planshet stol 5 da qoladi. Shunday tuzatingki:

  • table o'zgarganda eski stoldan uzilib, yangisiga ulansin;
  • StrictMode yoqilgan holda ham oxirida faqat bitta ulanish ochiq qolsin;
  • konsolda har qadam ko'rinsin (connect o'zi yozadi).

Ishora: effekt nuqtai nazaridan o'ylang — "men qaysi qiymat bilan sinxronlayapman?" va «Reaktiv qiymatlar» jadvali.

jsx
import { StrictMode, useEffect, useState } from "react";
import { createRoot } from "react-dom/client";

let open = 0;

function connect(table) {
  open++;
  console.log(`ulandi: ${table} (ochiq: ${open})`);
  return () => {
    open--;
    console.log(`uzildi: ${table} (ochiq: ${open})`);
  };
}

function Waiter({ table }) {
  useEffect(() => {
    connect(table);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
  return <p>Stol {table}</p>;
}

function App() {
  const [table, setTable] = useState(5);
  return (
    <div>
      <Waiter table={table} />
      <button onClick={() => setTable(7)}>Stol 7</button>
    </div>
  );
}

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

Konsolda:

text
ulandi: 5 (ochiq: 1)
ulandi: 5 (ochiq: 2)
Yechim
jsx
import { StrictMode, useEffect, useState } from "react";
import { createRoot } from "react-dom/client";

let open = 0;

function connect(table) {
  open++;
  console.log(`ulandi: ${table} (ochiq: ${open})`);
  return () => {
    open--;
    console.log(`uzildi: ${table} (ochiq: ${open})`);
  };
}

function Waiter({ table }) {
  useEffect(() => connect(table), [table]);
  return <p>Stol {table}</p>;
}

function App() {
  const [table, setTable] = useState(5);
  return (
    <div>
      <Waiter table={table} />
      <button onClick={() => setTable(7)}>Stol 7</button>
    </div>
  );
}

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

Konsolda:

text
ulandi: 5 (ochiq: 1)
uzildi: 5 (ochiq: 0)
ulandi: 5 (ochiq: 1)
uzildi: 5 (ochiq: 0)
ulandi: 7 (ochiq: 1)

Ikki xato bitta tuzatish bilan ketdi: cleanup qaytarildi (ochiq ulanishlar 1 dan oshmaydi) va massiv halol bo'ldi ([table] — stol almashganda qayta ulanish). Konsolning dastlabki uch qatori — StrictMode sinovi, keyingi ikkitasi — «Stol 7». eslint-disable izohi olib tashlandi: endi u kerak emas.

4-mashq: Vazifalar kuzatuvi

vazifalar ni npm run dev bilan oching, DevTools → Network → Fetch/XHR. Bitta ochilishda nechta /vazifalar/ so'rovi bor va qaysi biri (canceled)? Keyin npm run build && npm run preview bilan xuddi shuni qiling.

Yechim

Dev'da: ikki GET, birinchisi (canceled) — StrictMode'ning setup → cleanup → setup sinovi va AbortController cleanup'i. Agar serverda kalit hali bo'lmasa, keyin bitta PUT. Preview'da: bitta GET (StrictMode production build'da ishlamaydi). Ikkala holatda ham ekrandagi ro'yxat bir xil — foydalanuvchi farq sezmaydi, demak effekt to'g'ri.

10. Real ishda

  • StrictMode — Vite shabloni, Next.js (App Router'da sukut bo'yicha yoqilgan) va ko'p loyihalarda dev'da yoqiq. "Konsolda ikki marta" — yangi kelgan dasturchining birinchi savollaridan biri.
  • Analytics kutubxonalari (Google Analytics, Yandex Metrika, PostHog — Analitika darsida) dev'da odatda o'chiriladi yoki test rejimiga o'tkaziladi — ikki marta yuborish shu bilan ham hal bo'ladi.
  • Kod ko'rigida "StrictMode'ni o'chirdim, ikki marta so'rov ketayotgan edi" — qizil bayroq: cleanup yoki effektning o'zi noto'g'ri.
  • Intervyu savollari: "Nega useEffect dev'da ikki marta ishlaydi?", "useEffect(fn, []) va componentDidMount bir xilmi?", "StrictMode production'ga ta'sir qiladimi?".

Xulosa

  • Effekt — komponent hayoti emas, bitta sinxronlash: boshlash (setup) va to'xtatish (cleanup). Komponent ekranda turganda u ko'p marta qayta boshlanishi mumkin.
  • Effekt ichida o'qilgan har reaktiv qiymat (props, state, ulardan hisoblangan) — massivda; tashqi const, set funksiyalari va ref — reaktiv emas.
  • StrictMode dev'da har effektni bir marta setup → cleanup → setup qiladi. Foydalanuvchi farq sezsa — cleanup'ni tuzating; production'da takror yo'q.
  • Analytics'ning dev'dagi takrori — normal; xarid kabi harakat — effektda emas, hodisa ishlovchisida; ilova sozlamasi — modul darajasida.
  • StrictMode'ni o'chirish va "bir marta ishladimi" bayrog'i — xatoni yashiradi, tuzatmaydi.

Keyingi dars: Sizga effekt kerak emas — eng keng tarqalgan React xatosi: hisoblanadigan qiymat va harakat uchun effekt. vazifalar da o'lchov: effekt 2 soniyada 824 xabar yubordi.

Manbalar

  • React: "Lifecycle of Reactive Effects" — react.dev/learn/lifecycle-of-reactive-effects
  • React: "Synchronizing with Effects" — "How to handle the Effect firing twice in development?" — react.dev/learn/synchronizing-with-effects
  • React: <StrictMode> — react.dev/reference/react/StrictMode
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Effekt hayot sikli va StrictMode: nega useEffect dev'da ikki marta ishlaydi — IlmHamroh