Mundarija (29)
- Bu darsda
- 1. Nega bu kerak?
- 2. Bosh misol: 824 xabar
- 2.1 Uch variant
- 2.2 Nima bo'ldi — qadamma-qadam
- 2.3 To'g'ri joy — hodisa ishlovchisi
- 3. Hosilaviy qiymat — chizish paytida
- 3.1 Effekt bilan: ikki chizish
- 3.2 Renderda: bitta chizish
- 3.3 ESLint ham ko'radi
- 4. Harakat — hodisa ishlovchisida
- 4.1 "Savatga qo'shildi" xabari
- 4.2 Serverga yuborish
- 5. Prop o'zgarganda state
- 5.1 Butun state'ni tozalash — key
- 5.2 State'ning bir qismi — saqlamang, hisoblang
- 6. Effektlar zanjiri va ota'ga xabar
- 6.1 Zanjir — bitta ishlovchiga
- 6.2 Ota'ga xabar berish
- 7. Qaror jadvali
- 8. Vazifalar o'lchovi: effekt nimani biladi
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): effekt kerakmi?
- 2-mashq (o'rta): qidiruv natijasi
- 3-mashq (qiyin): buyurtma tugmasi
- 11. Real ishda
- Xulosa
- Manbalar
Sizga effekt kerak emas: React'da eng keng tarqalgan xato va uning o'rniga nima
Qisqacha: Effekt — faqat React'dan tashqaridagi tizim bilan sinxronlash uchun. Boshqa hamma holatda u ortiqcha chizish, eskirgan oraliq holat va sikl keltiradi. Boshqa state yoki props'dan hisoblanadigan qiymat — chizish paytida oddiy
constbilan hisoblanadi. Foydalanuvchi harakatiga javob (xabar, serverga yuborish, boshqa tablarga e'lon) — hodisa ishlovchisida bo'ladi. Prop almashganda state'ni tozalash —keybilan, ota'ga xabar berish — o'sha ishlovchida. Asosiy savol — bu kod nega ishlashi kerak: komponent ekranda ko'ringani uchunmi yoki foydalanuvchi biror narsa qilgani uchunmi? O'lchov:vazifalarda boshqa tablarga yuborishni effektga ko'chirganimizda 2 soniyada har tab 824 ta xabar yubordi.
Bu darsda
- Effekt "nima o'zgardi"ni bilishini, lekin "nega"ni bilmasligini o'lchov bilan ko'rasiz.
- Hosilaviy qiymatni effektdan renderga ko'chirasiz va ortiqcha chizishni konsolda sanaysiz.
- Harakat bilan bog'liq kodni effektdan hodisa ishlovchisiga ko'chirasiz.
- Prop o'zgarganda state'ni
keybilan tozalaysiz, effektlar zanjirini bitta ishlovchiga yig'asiz. - ESLint'ning
set-state-in-effectqoidasini o'qiysiz.
Oldin bilishingiz kerak: Effekt hayot sikli, State'ning saqlanishi va key bilan reset, State strukturasini loyihalash, Oyna va tablar orasida aloqa.
1. Nega bu kerak?
Effektni o'rgangandan keyin u hamma muammoga yechimdek ko'rinadi. "X o'zgarganda Y ni qil" — effekt. "Sahifa ochilganda" — effekt. "Ota'ga xabar ber" — effekt. React jamoasi bu holatni shunchalik ko'p ko'rdiki, hujjatda alohida katta sahifa yozdi: "You Might Not Need an Effect" — "Sizga effekt kerak bo'lmasligi mumkin".
Sardor ham shu yo'ldan bordi. useEffect I darsida vazifalar dagi localStorage ga saqlash effektga ko'chgan edi: "ro'yxat qanday bo'lsa, xotirada ham shunday". Sardor mantiqiy davom ettirdi: "Boshqa tablarga yuborish ham shunga o'xshash-ku. Ro'yxat o'zgardi — tablarga ayt. Bitta effektga qo'yaman, serverdaBajar ichidagi qo'lda chaqiruv kerak bo'lmaydi."
Biz shu g'oyani haqiqatan sinab ko'rdik — ikki brauzer tabi, bitta katak bosildi, 2 soniya kuzatildi. Natija bugungi darsning bosh misoli bo'ldi.
Hayotiy o'xshatish: restoranda ikki ofitsiant bir-biriga radio orqali gapiradi. Qoida: "buyurtmalar ro'yxati o'zgarsa — radioda e'lon qil". Birinchi ofitsiant buyurtma qo'shdi va e'lon qildi. Ikkinchisi e'lonni eshitib, o'z ro'yxatini yangiladi. Ro'yxati o'zgardi — qoida bo'yicha u ham e'lon qildi! Birinchisi eshitdi, ro'yxatini yangiladi, yana e'lon qildi… Qoida "nima o'zgardi"ga qaraydi, "nega o'zgardi"ga emas.
2. Bosh misol: 824 xabar
2.1 Uch variant
vazifalar ning holat 28 build'ini Chrome 154 da ikki tabda ochdik, birinchisida bitta katakni bosdik va 2 soniya davomida har tab nechta BroadcastChannel xabari yuborgani va qabul qilganini sanadik. Uch variant:
- a — kanon: yuborish hodisa ishlovchisida (
serverdaBajarichida). Qabul qilgan tab qayta yubormaydi. - b — tajriba:
useEffect(() => { saqla(v); tablargaYubor(v) }, [vazifalar]). Qabul qiluvchi kelgan ro'yxatni joriysi bilan solishtiradi (farq bo'lmasa — o'sha massiv). - c — tajriba: b + qabul qiluvchi solishtirmaydi (v6 dagi
ozgargan()kabi, har doim almashtiradi).
Haqiqiy natija. Har qatorning faqat oxiriga qarang — ikki tab nechta xabar yuborgani:
a — kanon (holat 28): hodisa ishlovchisida yuboriladi 1-tab yubordi 1, 2-tab yubordi 0
b — useEffect(() => { saqla(v); tablargaYubor(v) }, [vazifalar]); qabul qiluvchi solishtiradi 1 / 1
c — b + qabul qiluvchi solishtirmaydi (v6 dagi ozgargan() kabi) 1-tab yubordi 824, 2-tab yubordi 824- a — ishlovchidakanon1 xabar
- b — effektda, solishtiribaks-sado 1 marta2 xabar
- c — effektda, solishtirmay824 + 8241 648 xabar
Manba: IlmHamroh o'lchovi: vazifalar holat 28, Chrome 154 headless, 2 tab, 2026-10-13
Grafikdagi ustunlarning farqiga qarang: bitta bosish — bir yarim mingdan ortiq xabar. Har xabar postMessage, JSON tahlili, setVazifalar, qayta chizish va localStorage ga yozish demakdir. Ikkala tab ham to'xtovsiz ishlaydi. Telefon batareyasi va protsessor — shu ishda. Sinov 2 soniyadan keyin to'xtatildi; o'z-o'zidan esa sikl hech qachon to'xtamasdi.
2.2 Nima bo'ldi — qadamma-qadam
sequenceDiagram
participant T1 as 1-tab
participant K as BroadcastChannel
participant T2 as 2-tab
T1->>T1: katak bosildi, setVazifalar
T1->>K: effekt: tablargaYubor
K->>T2: xabar
T2->>T2: setVazifalar (yangi massiv)
T2->>K: effekt: tablargaYubor ❌
K->>T1: xabar
T1->>T1: setVazifalar (yangi massiv)
T1->>K: effekt: tablargaYubor ❌Diagrammadagi belgili qatorlar — muammo. Effekt bir savolga javob beradi: "vazifalar o'zgardimi?" — ha, o'zgardi. Lekin u nega o'zgarganini bilmaydi: foydalanuvchi bosdimi yoki boshqa tabdan xabar keldimi. Ikkala holatda ham "yubor" deydi.
b variantda qabul qiluvchi solishtiradi: kelgan ro'yxat joriysi bilan bir xil bo'lsa, state o'zgarmaydi va effekt ishlamaydi. Shunda aks-sado bir marta qaytib, to'xtaydi (1 + 1). Bu "ishlaydi", lekin baribir ortiqcha xabar. Bundan tashqari, xavfsizlik endi effekt emas, boshqa joydagi solishtirishga bog'liq bo'lib qoldi. Kimdir keyinroq solishtirishni "soddalashtirsa" — c.
2.3 To'g'ri joy — hodisa ishlovchisi
Kanonda saqlash va yuborish ajratilgan. Saqlash — effektda: u idempotent (bir xil ro'yxatni ikki marta yozish — bir marta yozish bilan teng) va ro'yxat qayerdan o'zgarmasin, kerak. Yuborish — foydalanuvchi harakati sodir bo'lgan joyda, serverdaBajar ichida (kanon):
sinxronniTox(); // yo'ldagi ro'yxat endi eskirgan
const oldingi = vazifalar;
flushSync(() => setVazifalar(yangi));
tablargaYubor(yangi);
void yozuvniYubor(oldingi, yangi, serverda, s, elonQil);Boshqa tabdan kelgan xabar esa setVazifalar ni chaqiradi, lekin tablargaYubor ni emas. Kanondagi izoh buni aniq aytadi:
// #28: boshqa tablar. Cleanup — obunani bekor qiladi. Kelgan ro'yxat
// saqlanadi (effekt yuqorida), lekin QAYTA YUBORILMAYDI: yuborsak,
// tablar bir-biriga cheksiz javob qaytaradi (aks-sado, 17/#30)Ishlovchi "nega"ni biladi: u aynan foydalanuvchi bosgani uchun chaqirildi. Qoida shundan kelib chiqadi:
Maslahat: Kod foydalanuvchi biror narsa qilgani uchun ishlashi kerak bo'lsa — hodisa ishlovchisiga. Komponent ekranda ko'ringani uchun ishlashi kerak bo'lsa (va tashqi tizim bilan bog'liq bo'lsa) — effektga.
3. Hosilaviy qiymat — chizish paytida
3.1 Effekt bilan: ikki chizish
Eng ko'p uchraydigan ortiqcha effekt — bir state'dan boshqasini hisoblash. Savatdagi jami narx:
import { useEffect, useState } from "react";
const OSH = { name: "Osh", price: 35000 };
function App() {
const [items, setItems] = useState([{ name: "Choy", price: 5000 }]);
const [total, setTotal] = useState(0);
useEffect(() => {
setTotal(items.reduce((sum, item) => sum + item.price, 0));
}, [items]);
console.log("chizish: jami =", total);
const add = () => setItems([...items, OSH]);
return (
<div>
<p>Jami: {total} so'm</p>
<button onClick={add}>Osh +</button>
</div>
);
}Konsolda:
chizish: jami = 0
chizish: jami = 5000
chizish: jami = 5000
chizish: jami = 40000Ekranda:
Jami: 40000 so'm
Osh +To'rt chizish, ikkitasida noto'g'ri jami: ochilishda 0 (aslida 5 000), bosishdan keyin 5000 (aslida 40 000). Ekranda bu bir lahzalik "miltillash", sekin telefonda esa ko'zga ko'rinadi. Har o'zgarishda ikki chizish: birinchisi eskirgan total bilan, ikkinchisi effekt tuzatgandan keyin.
| Qadam | items |
total |
Ekranda |
|---|---|---|---|
| 1-chizish | Choy | 0 | Jami: 0 |
| effekt → 2-chizish | Choy | 5000 | Jami: 5000 |
| «Osh +» → 3-chizish | Choy, Osh | 5000 | Jami: 5000 |
| effekt → 4-chizish | Choy, Osh | 40000 | Jami: 40000 |
3.2 Renderda: bitta chizish
import { useState } from "react";
const OSH = { name: "Osh", price: 35000 };
function App() {
const [items, setItems] = useState([{ name: "Choy", price: 5000 }]);
// Hosilaviy qiymat: har chizishda items'dan hisoblanadi
const total = items.reduce((sum, item) => sum + item.price, 0);
console.log("chizish: jami =", total);
const add = () => setItems([...items, OSH]);
return (
<div>
<p>Jami: {total} so'm</p>
<button onClick={add}>Osh +</button>
</div>
);
}Konsolda:
chizish: jami = 5000
chizish: jami = 40000Ikki chizish, ikkalasi ham to'g'ri. Kod ham qisqaroq: bitta state va bitta effekt kamaydi. State strukturasini loyihalash darsidagi qoida aynan shu: boshqa state yoki props'dan hisoblanadigan narsa — state emas.
"Har chizishda hisoblash — sekin emasmi?" Odatda yo'q: o'nlab elementli reduce mikrosoniyalar oladi. vazifalar da hisob, teglar va ko'rinadigan ro'yxat har chizishda hisoblanadi — 1 000 vazifada ham INP "yaxshi" (Mini-ilova darsidagi o'lchov). Hisob haqiqatan og'ir bo'lsa (o'n minglab element) — natijani saqlab qo'yadigan useMemo bor (memo, useMemo, useCallback). U ham effekt emas.
3.3 ESLint ham ko'radi
eslint-plugin-react-hooks 7 da yangi qoida bor — set-state-in-effect. Birinchi variantni (Cart komponenti sifatida) tekshirdik (qisqartirildi). Chiqish uzun, lekin asosiysi — birinchi qator (error va xato nomi) hamda oxirgi ikki qator: ko'rsatkich ^^^^ qaysi chaqiruv ostida turibdi:
6:5 error Error: Calling setState synchronously within an effect can trigger cascading renders
Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.
> 6 | setTotal(items.reduce((sum, item) => sum + item.price, 0));
| ^^^^^^^^ Avoid calling setState() directly within an effect"Effekt ichida setState ni sinxron chaqirish zanjirli qayta chizishlarga olib keladi. Effektlar React va tashqi tizimlar (DOM'ni qo'lda yangilash, state kutubxonalari, platforma API'lari) orasidagi sinxronlash uchun. Effekt tanasi quyidagilardan birini qilishi kerak: tashqi tizimni React'ning eng so'nggi holati bilan yangilash; yoki tashqi tizimga obuna bo'lib, u o'zgarganda callback ichida setState chaqirish". Qoida error darajasida — bu darsning qisqacha mazmuni linterda.
Tekshirib ko'ring:
const [fullName, setFullName] = useState("")vauseEffect(() => setFullName(first + " " + last), [first, last]). Qanday soddalashtirasiz?
Javob
const fullName = first + " " + last; — bitta qator, state ham, effekt ham yo'q. fullName har doim first va last dan hisoblanadi, demak u alohida "xotira" emas.
4. Harakat — hodisa ishlovchisida
4.1 "Savatga qo'shildi" xabari
Jasur aka: "Mehmon taom qo'shganda, pastda «Savatga qo'shildi» degan xabar chiqsin." Sardor effekt yozdi: "cart o'zgarsa — xabar ko'rsat". Savat localStorage dan tiklanadi (useEffect I). Muammo sahifa ochilganda ko'rinadi:
import { useEffect, useState } from "react";
function App() {
// localStorage'dan tiklangan savat (soddalashtirilgan)
const [cart, setCart] = useState(["Choy"]);
const [notice, setNotice] = useState("");
useEffect(() => {
if (cart.length > 0) {
setNotice(`«${cart.at(-1)}» savatga qo'shildi`); // ❌
}
}, [cart]);
return (
<div>
<p>Savat: {cart.join(", ")}</p>
<p>{notice}</p>
<button onClick={() => setCart([...cart, "Osh"])}>Osh +</button>
</div>
);
}Ekranda:
Savat: Choy
«Choy» savatga qo'shildi
Osh +Mehmon hech narsa qo'shmagan — sahifani endi ochdi, lekin xabar "qo'shildi" deyapti. Effekt "savat o'zgardi" va "savat tiklandi" ni ajrata olmaydi. Xabarning sababi — bosish, demak u bosish ishlovchisida:
import { useState } from "react";
function App() {
const [cart, setCart] = useState(["Choy"]);
const [notice, setNotice] = useState("");
function addDish(dish) {
setCart([...cart, dish]);
setNotice(`«${dish}» savatga qo'shildi`); // ✅ bosish
}
return (
<div>
<p>Savat: {cart.join(", ")}</p>
<p>{notice}</p>
<button onClick={() => addDish("Osh")}>Osh +</button>
</div>
);
}Ekranda:
Savat: Choy, Osh
«Osh» savatga qo'shildi
Osh +Ochilishda xabar yo'q, bosishda — bor. Agar savat boshqa joydan ham to'ldirilsa ("Takrorlash" tugmasi), xabar mantiqini addDish ichida qoldirib, ikkala tugmadan uni chaqirasiz — umumiy kod funksiyada, effektda emas.
4.2 Serverga yuborish
Xuddi shunday: "Buyurtma berish" bosilganda POST — ishlovchida (Effekt hayot sikli darsidagi "xarid" misoli). Ishlovchida yana bir ustunlik: await natijasini kutib, xatoni o'sha joyda ko'rsatish mumkin. Effekt ichida esa "qaysi bosish uchun bu xato?" degan savol paydo bo'ladi.
Lekin "sahifa ochilganda analytics'ga yuborish" — effekt: uning sababi — sahifaning ko'rinishi. Farq yana o'sha savolda: nega?
5. Prop o'zgarganda state
5.1 Butun state'ni tozalash — key
Mehmon fikri formasi: Review komponenti taom dish ini props'dan oladi, ichida izoh matni (state). Mehmon boshqa taomga o'tsa — izoh tozalanishi kerak. Effekt bilan:
function Review({ dish }) {
const [text, setText] = useState("");
useEffect(() => {
setText(""); // ❌ avval eski izoh, keyin tozalanadi
}, [dish]);
…
}Bu yana ikki chizish va bir lahzalik eski matn. Toza usul — State'ning saqlanishi va key bilan reset darsidagidek: ota key beradi. Agar key o'zgarsa, React uni boshqa komponent deb hisoblaydi va state'ni noldan boshlaydi:
import { useState } from "react";
function Review({ dish }) {
const [text, setText] = useState("");
return (
<div>
<p>{dish} haqida: {text || "—"}</p>
<input
aria-label="Izoh"
value={text}
onChange={(e) => setText(e.target.value)}
/>
</div>
);
}
function App() {
const [dish, setDish] = useState("Osh");
return (
<div>
<Review key={dish} dish={dish} />
<button onClick={() => setDish("Manti")}>Manti</button>
</div>
);
}Ekranda:
Manti haqida: —
Manti"Juda mazali" Osh uchun yozilgan edi. Manti'ga o'tganda izoh tozalandi — effektsiz, bitta chizishda. Review ichida hech narsa o'zgarmadi: qaror ota'da, key={dish} da.
5.2 State'ning bir qismi — saqlamang, hisoblang
Ba'zan faqat bir qismni moslash kerak. Masalan, ro'yxatdan tanlangan taom. Ro'yxat o'zgarib, tanlangan taom undan yo'qolsa, tanlov bekor bo'lishi kerak. Effekt bilan "agar tanlangan ro'yxatda bo'lmasa — setSelected(null)" yoziladi. Yaxshiroq yo'l — obyektni emas, id'ni saqlash va tanlovni chizish paytida hisoblash: const selected = dishes.find((d) => d.id === selectedId) ?? null. Taom ro'yxatdan chiqsa, selected o'z-o'zidan null — hech qanday sinxronlash kerak emas. vazifalar dagi tahrirId aynan shunday: tahrirdagi vazifa obyekti emas, uning id'si.
6. Effektlar zanjiri va ota'ga xabar
6.1 Zanjir — bitta ishlovchiga
Sardorning buyurtma oqimi: status o'zgarsa effekt step ni o'zgartiradi, step o'zgarsa boshqa effekt notice ni… Har effekt — alohida chizish. Zanjirda besh effekt bo'lsa, bitta bosish besh chizish. Oqimni kuzatish ham qiyin: "nega notice o'zgardi?" degan savolga javob uchun uch effektni o'qish kerak.
Davosi: keyingi holatni ishlovchining o'zida hisoblash. Ishlovchi bosilganda hamma kerakli set… larni birdan chaqiradi — React ularni bitta chizishga birlashtiradi (batching).
6.2 Ota'ga xabar berish
Toggle komponenti o'z state'ini o'zgartiradi va ota'ga onChange orqali xabar beradi. Effekt bilan:
import { useEffect, useState } from "react";
function Toggle({ onChange }) {
const [isOn, setIsOn] = useState(false);
useEffect(() => {
console.log("effekt: ota'ga", isOn);
onChange(isOn); // ❌ ochilganda ham chaqiriladi
}, [isOn, onChange]);
return (
<button onClick={() => setIsOn(!isOn)}>Yetkazib berish</button>
);
}
function App() {
const [delivery, setDelivery] = useState(false);
console.log("App chizildi, delivery =", delivery);
return <Toggle onChange={setDelivery} />;
}Konsolda:
App chizildi, delivery = false
effekt: ota'ga false
effekt: ota'ga true
App chizildi, delivery = trueIkki muammo: ochilishda ota'ga keraksiz false yuborildi. Bosishda esa avval Toggle chizildi, keyin effekt ota'ni o'zgartirdi va ikkinchi chizish bo'ldi.
| Render | Commit | Effekt | Cleanup |
|---|---|---|---|
ochilish: App (false), Toggle |
DOM | onChange(false) — keraksiz |
— |
bosish: Toggle (true) |
— | onChange(true) |
— |
App (true) — ikkinchi chizish |
DOM | — | — |
Ishlovchida esa bosish bitta qatorga sig'adi: ikkala set… birga, bitta render va bitta commit. Ishlovchida — ikkalasi bir vaqtda:
import { useState } from "react";
function Toggle({ onChange }) {
const [isOn, setIsOn] = useState(false);
function toggle() {
const next = !isOn;
setIsOn(next);
onChange(next); // ✅ bosish paytida, bitta chizish
}
return <button onClick={toggle}>Yetkazib berish</button>;
}
function App() {
const [delivery, setDelivery] = useState(false);
console.log("App chizildi, delivery =", delivery);
return <Toggle onChange={setDelivery} />;
}Konsolda:
App chizildi, delivery = false
App chizildi, delivery = trueBundan ham sodda yo'l — state'ni ota'ga ko'tarish (State'ni ko'tarish): Toggle o'z state'isiz, isOn ni props'dan oladi. Unda sinxronlashning o'zi kerak bo'lmaydi.
7. Qaror jadvali
Har effekt yozishdan oldin jadvalga qarang:
| Vazifa | Effekt kerakmi | O'rniga |
|---|---|---|
| state/props'dan hisoblanadigan qiymat | renderda const (og'ir bo'lsa — useMemo) |
|
| prop o'zgarganda hamma state'ni tozalash | key |
|
| prop o'zgarganda state'ning bir qismi | id saqlab, renderda hisoblash | |
| foydalanuvchi harakatiga javob | hodisa ishlovchisi | |
| ota'ga xabar berish | ishlovchida yoki state'ni ko'tarish | |
| ilovani bir marta sozlash | modul darajasi (Effekt hayot sikli) | |
| tashqi manbaga obuna (online, URL) | useSyncExternalStore (useSyncExternalStore) |
|
| ma'lumot olish | kutubxona yoki framework (Effektda ma'lumot olish) | |
document.title, localStorage, taymer, WebSocket |
effekt + cleanup |
belgilisi — effekt bilan ham yoziladi va ishlaydi, lekin keyingi darslarda yaxshiroq vosita bor. Ma'lumot olish uchun ko'pincha kutubxona (TanStack Query, SWR) yoki framework (Next.js, React Router) ishlatiladi — ular effektning keshi, poygasi va takrorini o'zlari hal qiladi (TanStack Query I, Server Components).
8. Vazifalar o'lchovi: effekt nimani biladi
Bu dars vazifalar kodini o'zgartirmaydi. Kanonda keraksiz effekt yo'q: oltitasining har biri tashqi tizim bilan sinxronlaydi (Effekt hayot sikli dagi jadval). O'rniga «Bosh misol» bo'limidagi o'lchovni qanday qilganimizni aytamiz — uni o'zingiz ham takrorlashingiz mumkin.
- Holat 28 dan ikki vaqtinchalik nusxa: b —
App.tsxdagi saqlash effektigatablargaYubor(v)qo'shildi,serverdaBajardagi chaqiruv olib tashlandi. c — b ga qo'shimcha, qabul qiluvchi solishtirmaydi (setVazifalar(yangi)). - Har nusxa build qilindi va statik serverda ochildi.
- Puppeteer bilan ikki tab ochildi. Har tabda
BroadcastChannel.prototype.postMessagesanagichga o'raldi (window.__yuborildi++). - 1-tabda
#vazifa-1katagi bosildi, 2 soniya kutildi, sanagichlar o'qildi.
Natija «Bosh misol» bo'limidagi grafikda. Kanon qarori shundan: boshqa tablarga yuborish — hodisa ishlovchisida, effektda emas; localStorage ga yozish — effektda (idempotent sinxron). Vaqtinchalik nusxalar o'chirildi.
Diqqat: Bu o'lchovda effektning o'zi "noto'g'ri" emas edi — u o'z ishini qildi: "ro'yxat o'zgardi → tashqi tizimga ayt". Xato — vazifani noto'g'ri joyga qo'yishda. Tablarga yuborish "sinxronlash" emas, balki "e'lon qilish", ya'ni harakat.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| bir lahza eski qiymat, keyin to'g'ri | hosilaviy qiymat effektda | renderda hisoblang |
Calling setState synchronously within an effect |
effektda setState |
jadvaldan to'g'ri joyni toping |
| sahifa ochilganda "qo'shildi" xabari | harakat xabari effektda | ishlovchiga |
| prop almashganda eski matn miltillaydi | state effektda tozalanadi | key |
| tablar orasida cheksiz xabar | e'lon effektda | e'lon — ishlovchida, qabul qilgan qayta yubormaydi |
| bitta bosishda 3–5 chizish | effektlar zanjiri | hammasini ishlovchida hisoblang |
10. Mashqlar
Mashqlar kurs/mashqlar/17/30-effekt-kerak-emas/ da. Har yechimni «Ishga tushir» bilan sinang.
1-mashq (oson): effekt kerakmi?
Har vazifa uchun javob bering: effekt kerakmi (ha/yo'q)?
- Taomlar ro'yxatidan faqat ichimliklarni ko'rsatish:
- Tanlangan taom nomini tab sarlavhasiga (
document.title) yozish: - «Bron qilish» bosilganda serverga POST yuborish:
- Sahifa ochiq turganda har 30 soniyada buyurtmalar holatini so'rash:
Yechim
1 — filtrlangan ro'yxat hisoblanadi: renderda dishes.filter(…). 2 — document.title tashqi tizim: effekt. 3 — sabab bosish: ishlovchi. 4 — taymer (setInterval) — tashqi tizim, effekt va cleanup (clearInterval); keyinroq bu ishni kutubxona ham qila oladi (TanStack Query I).
2-mashq (o'rta): qidiruv natijasi
Menyu qidiruvi: query state'i va topilgan taomlar soni. Sardor topilganlarni state'da saqlab, effekt bilan to'ldirdi. Konsolda har harfda ikki chizish. Effekt va found state'ini olib tashlang: topilganlar renderda hisoblansin, konsolda har o'zgarishga bitta chizish bo'lsin.
import { useEffect, useState } from "react";
const MENU = ["Osh", "Lag'mon", "Manti", "Ko'k choy", "Qora choy"];
function App() {
const [query, setQuery] = useState("");
const [found, setFound] = useState(MENU);
useEffect(() => {
const q = query.toLowerCase();
setFound(MENU.filter((d) => d.toLowerCase().includes(q)));
}, [query]);
console.log("chizish:", query, found.length);
return (
<div>
<input
placeholder="Qidiruv"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<p>Topildi: {found.length}</p>
</div>
);
}Konsolda:
chizish: 5
chizish: 5
chizish: ch 5
chizish: ch 2Yechim
import { useState } from "react";
const MENU = ["Osh", "Lag'mon", "Manti", "Ko'k choy", "Qora choy"];
function App() {
const [query, setQuery] = useState("");
const q = query.toLowerCase();
const found = MENU.filter((d) => d.toLowerCase().includes(q));
console.log("chizish:", query, found.length);
return (
<div>
<input
placeholder="Qidiruv"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<p>Topildi: {found.length}</p>
</div>
);
}Konsolda:
chizish: 5
chizish: ch 2Ekranda:
Topildi: 2Effektli variantda ochilishda ikki chizish (effekt found ni yangi massivga almashtirdi) va yozishda ikki chizish, biri eskirgan (ch 5). Renderda hisoblash bilan — har o'zgarishga bitta, eskirgan oraliq holat yo'q. vazifalar dagi qidiruv ham shunday: ko'rinadigan vazifalar har chizishda hisoblanadi.
3-mashq (qiyin): buyurtma tugmasi
OrderButton mehmon «Buyurtma berish» ni bosganda serverga yuboradi. Sardor buni effektga qo'ydi: submitted state'i true bo'lsa — yubor. Ikki muammo bor: (1) yuborish bosish bilan emas, holat bilan bog'langan; (2) StrictMode'da ikki marta yuboriladi. Kodni shunday tuzatingki:
sendOrderfaqat bosilganda, bir marta chaqirilsin (StrictMode'da ham);- effekt qolmasin;
- ekranda yuborilgandan keyin
Buyurtma yuborildichiqsin.
Ishora: «Harakat — hodisa ishlovchisida» bo'limi.
import { StrictMode, useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
function sendOrder(dish) {
console.log("POST /buyurtma:", dish); // haqiqiy ilovada — fetch
}
function OrderButton({ dish }) {
const [submitted, setSubmitted] = useState(false);
useEffect(() => {
if (submitted) {
sendOrder(dish); // ❌
}
}, [submitted, dish]);
if (submitted) return <p>Buyurtma yuborildi</p>;
return (
<button onClick={() => setSubmitted(true)}>
Buyurtma berish
</button>
);
}
createRoot(document.getElementById("root")).render(
<StrictMode>
<OrderButton dish="Osh" />
</StrictMode>,
);Konsolda:
POST /buyurtma: OshBu yerda StrictMode takrori ko'rinmadi: sinov faqat birinchi ochilishda, submitted esa o'shanda false edi. Lekin effekt sahifa qayta ochilganda (masalan, submitted keyinroq localStorage dan tiklansa) yana yuborardi — sababi bosish emas, holat.
Yechim
import { StrictMode, useState } from "react";
import { createRoot } from "react-dom/client";
function sendOrder(dish) {
console.log("POST /buyurtma:", dish); // haqiqiy ilovada — fetch
}
function OrderButton({ dish }) {
const [submitted, setSubmitted] = useState(false);
function handleClick() {
sendOrder(dish); // ✅ sabab — bosish
setSubmitted(true);
}
if (submitted) return <p>Buyurtma yuborildi</p>;
return <button onClick={handleClick}>Buyurtma berish</button>;
}
createRoot(document.getElementById("root")).render(
<StrictMode>
<OrderButton dish="Osh" />
</StrictMode>,
);Konsolda:
POST /buyurtma: OshEkranda:
Buyurtma yuborildiNatija bir xil ko'rinadi, lekin ma'nosi boshqa: endi sendOrder faqat bosish bilan bog'liq. Hech qanday holat (tiklash, qayta chizish, StrictMode) uni qayta chaqira olmaydi. submitted state'i faqat ekranni almashtirish uchun qoldi.
11. Real ishda
- Kod ko'rigida eng ko'p yoziladigan izoh React loyihalarida: "Bu effekt kerak emas — renderda hisoblang" yoki "bu ishlovchiga tegishli". React jamoasi ESLint'ga
set-state-in-effectqoidasini aynan shu sababli qo'shdi. - React Compiler (React Compiler) ortiqcha effektlarni tuzatmaydi — u faqat hisoblashlarni saqlab qo'yadi. Effektni olib tashlash dasturchining ishi.
- Tablar orasida aloqa (BroadcastChannel,
storagehodisasi), WebSocket va server push — aks-sado xavfi bor har joyda bitta qoida: kelgan xabarga javoban qayta e'lon qilmang. - Intervyu savollari: "Effekt qachon kerak emas? Uch misol ayting", "hosilaviy qiymatni (derived state) state'da saqlash nega yomon?", "prop o'zgarganda state'ni qanday tozalaysiz?" (javob:
key).
Xulosa
- Effekt — faqat tashqi tizim bilan sinxronlash uchun. Savol: kod komponent ko'ringani uchunmi yoki foydalanuvchi harakati uchun ishlaydi?
- Hosilaviy qiymat — renderda: effektda u ortiqcha chizish va eskirgan oraliq holat beradi.
- Harakat (xabar, POST, tablarga e'lon) — hodisa ishlovchisida. O'lchov: effektdagi e'lon 2 soniyada har tabda 824 xabar.
- Prop o'zgarganda hamma state —
keybilan; bir qismi — id saqlab, renderda hisoblash. - Effektlar zanjiri va ota'ga xabar — bitta ishlovchida; ESLint
set-state-in-effectbu xatolarni ushlaydi.
Keyingi dars: useEffectEvent: reaktiv bo'lmagan mantiq — effekt ichida eng so'nggi qiymatni o'qish, lekin shu qiymat uchun effektni qayta ishga tushirmaslik.
Manbalar
- React: "You Might Not Need an Effect" — react.dev/learn/you-might-not-need-an-effect
- React: "Separating Events from Effects" — react.dev/learn/separating-events-from-effects
eslint-plugin-react-hooks7.1.1:set-state-in-effect— react.dev/reference/eslint-plugin-react-hooks
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!