Mundarija (30)
- Bu darsda
- 1. Nega bu kerak?
- 2. To'rt joy — qaysi biri to'g'ri
- 2.1 let — har chizishda yo'qoladi
- 2.2 Modul o'zgaruvchisi — hamma uchun bitta
- 2.3 State — ishlaydi, lekin ortiqcha
- 2.4 Ref — aynan kerakli
- 3. useRef qanday ishlaydi
- 3.1 Quti
- 3.2 Ref o'zgaradi — ekran yo'q
- 4. Qoida: chizish paytida ref'ga tegmang
- 4.1 Nega
- 4.2 Yagona istisno: lazy init (dangasa boshlash)
- 5. Oldingi qiymat
- 5.1 Effektda solishtirish
- 5.2 Ekranda kerak bo'lsa — state
- 6. TypeScript'da ref
- 7. Vazifalar qadami: modul o'zgaruvchilari ref'larga
- 7.1 Reja
- 7.2 E'lon taymeri
- 7.3 Server sinxronining xo'jaligi
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): state yoki ref?
- 2-mashq (o'rta): bosishlar statistikasi
- 3-mashq (qiyin): ikki stolning eslatmasi
- 4-mashq: Vazifalar qadami
- 10. Real ishda
- Xulosa
- Manbalar
useRef I: qayta chizish chaqirmaydigan xotira — taymer id, hisoblagich, oldingi qiymat
Qisqacha:
useRef(boshlang'ich)— komponentga{ current: … }qutisini beradi. Quti har chizishda o'sha obyekt,ref.currentni istagancha o'zgartirsa bo'ladi va bu qayta chizish chaqirmaydi. U ekranda ko'rinmaydigan, lekin chizishlar orasida eslab qolinishi kerak bo'lgan narsalar uchun: taymer id'si,AbortController, bosishlar soni, oldingi qiymat. Oddiylethar chizishda yo'qoladi, modul o'zgaruvchisi esa hamma nusxalar uchun bitta. State esa har o'zgarishda qayta chizadi. Qoida:ref.currentni chizish paytida o'qimang va yozmang — faqat hodisa ishlovchisi va effektlarda.
Bu darsda
- Chizishlar orasida qiymat saqlashning to'rt usulini (
let, modul o'zgaruvchisi, state, ref) taqqoslay olasiz. useRefbilan taymer id'sini saqlab, uni to'g'ri to'xtatasiz.- Ref o'zgarganda nega ekran yangilanmasligini tushuntira olasiz.
- "Chizish paytida ref'ga tegmaslik" qoidasini va ESLint
react-hooks/refsxabarini bilasiz. vazifalardagi modul o'zgaruvchilarini (elonTaymeri, sinxron xo'jaligi) ref'larga ko'chirasiz.
Oldin bilishingiz kerak: useEffect III: cleanup, State I: useState, Pure komponentlar va render qoidasi, Taymerlar.
1. Nega bu kerak?
useEffect III darsida taymer effekt ichida yashardi: id effektning o'zgaruvchisida, cleanup uni ko'radi. Lekin taymerni foydalanuvchi boshqarsa-chi? Jasur aka choyxona uchun tugma so'radi: "«Eslatma» bosilsa, 3 daqiqadan keyin «Choy tayyor!» chiqsin. Mehmon fikridan qaytsa — «Bekor qilish»."
Taymer ishlovchida boshlanadi (setTimeout), boshqa ishlovchida to'xtatiladi (clearTimeout(id)). Demak id ikki ishlovchi orasida, ya'ni ikki xil chizish orasida eslab qolinishi kerak. Qayerga saqlaymiz?
vazifalar da bu savol allaqachon turibdi. E'lon matni 3 soniyadan keyin yo'qoladi (elonTaymeri) va u hozircha modul o'zgaruvchisi. Kanondagi izoh buni ochiq aytadi: "Hozircha modul o'zgaruvchisi (TEXNIK-QARZ 29) — 17/#34 da useRef". Server sinxronining "pulti" (sinxronPulti) va tugamagan yozuvlar soni (kutilmoqda) ham shunday. Bugun bu qarzni to'laymiz.
Hayotiy o'xshatish: oshpazning cho'ntak daftarchasi. Unga "3-stol choyi — 14:05 da" deb yozib qo'yadi. Daftarcha zalda hech kimga ko'rinmaydi va unga yozish menyu taxtasini o'zgartirmaydi. Lekin oshpaz uni smena bo'yi o'zida olib yuradi. Menyu taxtasi — state (o'zgarsa, hamma ko'radi). Cho'ntak daftarcha — ref.
2. To'rt joy — qaysi biri to'g'ri
2.1 let — har chizishda yo'qoladi
Birinchi urinish — komponent ichidagi oddiy o'zgaruvchi:
import { useState } from "react";
function App() {
const [waiting, setWaiting] = useState(false);
let timerId; // ❌ har chizishda yangidan undefined
function remind() {
timerId = setTimeout(() => console.log("Choy tayyor!"), 300);
setWaiting(true);
}
function cancel() {
clearTimeout(timerId);
setWaiting(false);
console.log("bekor qilindi, timerId =", timerId);
}
return (
<div>
<p>{waiting ? "Kutilmoqda…" : "Eslatma yo'q"}</p>
<button onClick={remind}>Eslatma</button>{" "}
<button onClick={cancel}>Bekor qilish</button>
</div>
);
}Konsolda:
bekor qilindi, timerId = undefined
Choy tayyor!"Bekor qilindi" deyildi, lekin choy baribir "tayyor". Sabab: setWaiting(true) qayta chizishga olib keldi. Komponent funksiyasi boshidan chaqirildi va let timerId yana undefined bo'ldi. cancel — yangi chizishning funksiyasi va u yangi, bo'sh timerId ni ko'radi. Taymer id'si birinchi chizish bilan birga yo'qoldi.
| Qadam | Chizish | timerId |
Nima bo'ldi |
|---|---|---|---|
| «Eslatma» | 1-chizishning remind |
12 |
taymer boshlandi, setWaiting(true) |
| qayta chizish | 2-chizish | undefined |
yangi let |
| «Bekor qilish» | 2-chizishning cancel |
undefined |
clearTimeout(undefined) — hech narsa |
| 300 ms | — | — | "Choy tayyor!" |
(12 — namuna: brauzer taymerga istalgan raqam beradi.)
2.2 Modul o'zgaruvchisi — hamma uchun bitta
Ikkinchi urinish — let timerId ni komponentdan tashqariga chiqarish. Endi u yo'qolmaydi. Lekin choyxonada ikki stol bor va har stolda o'z Reminder i:
import { useState } from "react";
let timerId; // ❌ modul darajasida — ikkala Reminder uchun BITTA
function Reminder({ table }) {
const [waiting, setWaiting] = useState(false);
function remind() {
const done = () => console.log(`Choy tayyor! (${table})`);
timerId = setTimeout(done, 300);
setWaiting(true);
}
function cancel() {
clearTimeout(timerId);
setWaiting(false);
}
return (
<p>
{table}: {waiting ? "kutilmoqda" : "—"}{" "}
<button onClick={remind}>Eslatma {table}</button>{" "}
<button onClick={cancel}>Bekor {table}</button>
</p>
);
}
function App() {
return (
<div>
<Reminder table="A" />
<Reminder table="B" />
</div>
);
}Konsolda:
Choy tayyor! (A)A stoli bekor qildi — lekin A'ning choyi "tayyor" bo'ldi, B'niki esa jim yo'qoldi. B'ning remind i umumiy timerId ni o'z taymeri bilan almashtirgan edi. «Bekor A» esa B'ning taymerini to'xtatdi. Modul o'zgaruvchisi komponentning har nusxasiga emas, butun faylga tegishli. vazifalar dagi elonTaymeri ning muammosi ham shu: hozir App bitta, lekin testlarda (bir faylda bir necha render(<App />)) yoki kelajakda ikkita App bo'lsa, ular bitta taymerni bo'lishadi.
2.3 State — ishlaydi, lekin ortiqcha
Uchinchi urinish — const [timerId, setTimerId] = useState(null). Ishlaydi: state chizishlar orasida saqlanadi va har nusxaning o'z state'i bor. Lekin setTimerId(id) qayta chizish chaqiradi — taymer id'si ekranda ko'rinmasa ham. Har eslatmada bitta keraksiz chizish. Bundan tashqari, state "snapshot" (State II): setTimerId dan keyin o'sha ishlovchida timerId hali eski qiymat.
2.4 Ref — aynan kerakli
To'rtinchi joy — useRef:
import { useRef, useState } from "react";
function App() {
const [waiting, setWaiting] = useState(false);
const timerRef = useRef(null); // { current: null } — doim O'SHA
function remind() {
const done = () => console.log("Choy tayyor!");
timerRef.current = setTimeout(done, 300);
setWaiting(true);
}
function cancel() {
clearTimeout(timerRef.current);
setWaiting(false);
console.log("bekor qilindi");
}
return (
<div>
<p>{waiting ? "Kutilmoqda…" : "Eslatma yo'q"}</p>
<button onClick={remind}>Eslatma</button>{" "}
<button onClick={cancel}>Bekor qilish</button>
</div>
);
}Konsolda:
bekor qilindiEkranda:
Eslatma yo'q
Eslatma Bekor qilish"Choy tayyor!" yo'q — taymer haqiqatan to'xtadi. Qayta chizishdan keyin ham timerRef — o'sha obyekt va uning current ida birinchi chizishda yozilgan id turibdi.
Yig'ma jadval:
| Joy | Chizishlar orasida saqlanadimi | Har nusxaga alohidami | O'zgarsa qayta chizadimi |
|---|---|---|---|
let (komponent ichida) |
— | ||
| modul o'zgaruvchisi | |||
useState |
|||
useRef |
Ref — yagona qator, uchala ustunda ham kerakli javob bor.
Qaror savollar bilan:
flowchart TD
Q1{"O'zgarsa, ekran yangilanishi kerakmi?"}
Q1 -- "ha" --> S["useState"]
Q1 -- "yo'q" --> Q2{"Chizishlar orasida eslab qolish kerakmi?"}
Q2 -- "yo'q" --> L["oddiy const / let"]
Q2 -- "ha" --> Q3{"Har nusxaga alohidami?"}
Q3 -- "ha" --> R["useRef"]
Q3 -- "yo'q, butun ilovaga bitta" --> M["modul darajasi"]Diagrammani yuqoridan o'qing: birinchi savol — eng muhimi. Ekranga ta'sir qiladigan narsa doim state. Qolganlari uchun ikki savol — va ko'pincha javob ref.
3. useRef qanday ishlaydi
3.1 Quti
Ref (reference — havola) — React komponentga beradigan oddiy obyekt: { current: boshlang'ich }. useRef(0) birinchi chizishda { current: 0 } ni yaratadi va keyingi har chizishda aynan o'sha obyektni qaytaradi. Uning current xususiyatini siz istagancha o'qiysiz va yozasiz — React bunga aralashmaydi va kuzatmaydi ham.
React ichida useRef ni useState orqali yozsa bo'lardi:
// Tushunish uchun model (React aslida shunga yaqin):
function useRef(initial) {
const [ref] = useState({ current: initial }); // set… chaqirilmaydi
return ref;
}State ichidagi obyekt — har chizishda o'sha. Biz uning ichini o'zgartiramiz, lekin setRef ni chaqirmaymiz — shuning uchun React qayta chizmaydi. Obyekt va massivlarni immutable yangilash darsida "state obyektini joyida o'zgartirmang, React sezmaydi" degan edik. Ref aynan shu xususiyatni foydali tomonga buradi: sezmasligi kerak bo'lgan narsa uchun.
3.2 Ref o'zgaradi — ekran yo'q
import { useRef } from "react";
function App() {
const clicks = useRef(0);
console.log("chizildi");
function add() {
clicks.current++;
console.log("ref:", clicks.current);
}
return (
<div>
<button onClick={add}>+1</button>{" "}
<button onClick={() => console.log("jami:", clicks.current)}>
Ko'rsat
</button>
</div>
);
}Konsolda:
chizildi
ref: 1
ref: 2
jami: 2"chizildi" — faqat bir marta, ochilishda. Ikki bosish clicks.current ni 2 ga yetkazdi, lekin komponent qayta chizilmadi. Agar clicks.current ni JSX'da ko'rsatganimizda, ekranda abadiy 0 turardi. Shuning uchun ref — ekranda ko'rinmaydigan narsa uchun.
Tekshirib ko'ring:
useRefqaytargan obyektni effekt massiviga yozish kerakmi?timerRef.currentni-chi?
Javob
timerRef obyektini — yo'q: u barqaror, har chizishda o'sha (useEffect II darsidagi "barqaror qiymatlar"). ESLint ham so'ramaydi. timerRef.current ni esa massivga yozish ma'nosiz: React massivni chizishdan keyin solishtiradi, current esa chizishdan tashqarida o'zgaradi va qayta chizish chaqirmaydi. Effekt uning o'zgarishini "ko'rmaydi".
4. Qoida: chizish paytida ref'ga tegmang
4.1 Nega
Ref'ni hodisa ishlovchisida va effektda o'qing va yozing. Chizish paytida (komponent tanasida, JSX'da) — yo'q. Sabab — Pure komponentlar qoidasi: komponent toza funksiya, bir xil kirish — bir xil natija. ref.current esa React bilmaydigan, istalgan payt o'zgaradigan qiymat. Uni JSX'da ishlatsangiz, ekran unga ergashmaydi (yuqoridagi 0). Chizish paytida yozsangiz — StrictMode ikki marta chaqiradi va qiymat ikki baravar o'sadi.
ESLint (plagin 7.1.1, React Compiler qoidasi) buni ushlaydi. Faylda chizishlarni ref bilan sanashga urinish (qisqartirildi):
export function Counter() {
const clicks = useRef(0);
const [, setN] = useState(0);
clicks.current = clicks.current + 1;
return <p onClick={() => setN(1)}>Chizildi: {clicks.current}</p>;
} 6:3 error Error: Cannot access refs during render
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
> 6 | clicks.current = clicks.current + 1;
| ^^^^^^^^^^^^^^ Cannot update ref during render"Chizish paytida ref'larga murojaat qilib bo'lmaydi. Ref'lar — chizish uchun kerak bo'lmagan qiymatlar. Ularga faqat chizishdan tashqarida — hodisa ishlovchilari yoki effektlarda — murojaat qiling. Chizish paytida current ni o'qish komponentning kutilgandek yangilanmasligiga olib keladi." Shu faylda yana ikki xato bor: 6-qatordagi o'qish va 7-qatordagi JSX'dagi clicks.current. Qoida nomi — react-hooks/refs.
4.2 Yagona istisno: lazy init (dangasa boshlash)
Bitta holatda chizish paytida ref'ga yozish mumkin: bir marta, bo'sh bo'lsa, qimmat obyektni yaratish:
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = new AudioPlayer(); // faqat birinchi chizishda
}useRef(new AudioPlayer()) deb yozsangiz, new AudioPlayer() har chizishda chaqiriladi (natijasi faqat birinchi marta olinadi). Bu shart — natija har doim bir xil, demak toza. React hujjati bu naqshni ruxsat etadi.
5. Oldingi qiymat
5.1 Effektda solishtirish
"Mehmon kategoriyani qaysidan qaysiga almashtirdi?" — analytics uchun. Oldingi qiymat ekranga chiqmaydi, faqat effektda kerak — ref uchun ideal:
import { useEffect, useRef, useState } from "react";
function App() {
const [category, setCategory] = useState("taom");
const prevRef = useRef(category);
useEffect(() => {
if (prevRef.current !== category) {
console.log(`o'tish: ${prevRef.current} → ${category}`);
}
prevRef.current = category; // keyingi safar uchun
}, [category]);
return (
<div>
<p>Bo'lim: {category}</p>
<button onClick={() => setCategory("ichimlik")}>
Ichimlik
</button>{" "}
<button onClick={() => setCategory("shirinlik")}>
Shirinlik
</button>
</div>
);
}Konsolda:
o'tish: taom → ichimlik
o'tish: ichimlik → shirinlikRef effekt ichida o'qiladi va yoziladi — chizishdan tashqarida. Ochilishda hech narsa chiqmadi: prevRef.current va category bir xil edi.
5.2 Ekranda kerak bo'lsa — state
Oldingi qiymat ekranda kerak bo'lsa ("narx 30 000 dan 35 000 ga oshdi ↑"), ref yaramaydi — chizish paytida o'qish taqiqlangan. U holda oldingi qiymat state'da saqlanadi. U chizish paytida, shart bilan yangilanadi — React hujjatidagi "oldingi chizishdagi ma'lumotni saqlash" naqshi:
import { useState } from "react";
function Price({ price }) {
const [prev, setPrev] = useState(price);
const [trend, setTrend] = useState("");
if (price !== prev) {
setPrev(price); // chizish paytida, faqat price o'zgarganda
setTrend(price > prev ? "↑" : "↓");
}
return <p>Osh: {price} so'm {trend}</p>;
}
function App() {
const [price, setPrice] = useState(30000);
return (
<div>
<Price price={price} />
<button onClick={() => setPrice(35000)}>Narxni oshir</button>
</div>
);
}Ekranda:
Osh: 35000 so'm ↑
Narxni oshirBu naqsh kam uchraydi va o'qish biroz qiyin. Avval o'ylang: balki oldingi qiymatni ota'da, hodisa paytida hisoblash mumkindir? Lekin ref'ni JSX'da o'qishdan ko'ra u to'g'ri.
6. TypeScript'da ref
useRef ning turi boshlang'ich qiymatdan olinadi. Taymer id'si uchun turni aniq yozish kerak — setTimeout brauzerda son, Node'da obyekt qaytaradi. ReturnType<typeof setTimeout> ikkalasiga ham to'g'ri (Utility types):
import { useRef, useState } from "react";
export default function App() {
const [notice, setNotice] = useState("");
const timer = useRef<ReturnType<typeof setTimeout>>(undefined);
function announce(text: string) {
clearTimeout(timer.current); // undefined bo'lsa ham xavfsiz
setNotice(text);
timer.current = setTimeout(() => setNotice(""), 3000);
}
return (
<button onClick={() => announce("Saqlandi")}>
Saqlash {notice}
</button>
);
}Ekranda:
SaqlashuseRef<T>(undefined) — React 19 turlarida boshlang'ich qiymat majburiy. 18-versiya turlarida useRef<T>() bo'sh qavslar bilan ham yozilardi — eski kodda uchraydi. Hozir u tip xatosi:
import { useRef } from "react";
export function App() {
const timer = useRef<number>(); // ❌ argumentsiz
return <p>{typeof timer}</p>;
}Xato:
TS2554: Expected 1 arguments, but got 0."1 ta argument kutilgan edi, 0 ta berildi." Tuzatish — useRef<number>(undefined) yoki, DOM ref'larida, useRef<HTMLInputElement>(null) (useRef II). clearTimeout(undefined) — xato emas, shunchaki hech narsa qilmaydi. Bu aynan vazifalar dagi elonQil ning shakli.
7. Vazifalar qadami: modul o'zgaruvchilari ref'larga
7.1 Reja
Yangi branch: refactor/reflar. Ikki dars (shu va useRef II) — bitta commit. Bugun — "xo'jalik" ref'lari: e'lon taymeri va server sinxronining pulti. Keyingi darsda — DOM ref'lari (fokus, dialog) va commit.
7.2 E'lon taymeri
-// Taymer id — ekranga chiqmaydi, render'ga ta'sir qilmaydi: state emas.
-// Hozircha modul o'zgaruvchisi (TEXNIK-QARZ 29) — 17/#34 da useRef
-let elonTaymeri: ReturnType<typeof setTimeout> | undefined;
+ // --- Ref'lar (17/#33–#34): o'zgarsa qayta chizilmaydigan xotira ---
+ // Taymer id — ekranga chiqmaydi (#28 da modul o'zgaruvchisi edi)
+ const elonTaymeri = useRef<ReturnType<typeof setTimeout>>(undefined);
@@
function elonQil(matn: string) {
- clearTimeout(elonTaymeri);
+ clearTimeout(elonTaymeri.current);
setElon(matn);
if (matn !== "") {
- elonTaymeri = setTimeout(() => setElon(""), ELON_MUDDATI);
+ elonTaymeri.current = setTimeout(() => setElon(""), ELON_MUDDATI);
}
}Birinchi uch qator modul darajasidan o'chdi, useRef esa App ichida, boshqa hooklar bilan birga paydo bo'ldi. elonQil — hodisa ishlovchilaridan chaqiriladi, demak ref chizishdan tashqarida o'qiladi va yoziladi.
7.3 Server sinxronining xo'jaligi
server-sinxron.ts da ikki modul o'zgaruvchisi bor edi: yo'ldagi so'rovning AbortController i va tugamagan yozuvlar soni. Ular bitta turga yig'ildi va App ning ref'iga ko'chdi (kanon):
/** Render'ga ta'sir qilmaydigan xotira — useRef ichida */
export type SinxronXojaligi = {
pult: AbortController | null; // yo'ldagi solishtirish so'rovi
kutilmoqda: number; // tugamagan yozuvlar
}; const xojalik = useRef<SinxronXojaligi>({
pult: null,
kutilmoqda: 0,
});Modul funksiyalari ref'ni argument sifatida oladi (RefObject<SinxronXojaligi>), o'zgaruvchiga to'g'ridan-to'g'ri murojaat qilmaydi:
-export function sinxronniTox(): void {
- sinxronPulti?.abort();
+export function sinxronniTox(
+ xojalik: RefObject<SinxronXojaligi>,
+): void {
+ xojalik.current.pult?.abort();
}Effekt cleanup'i ham o'zgardi: return sinxronniTox → return () => sinxronniTox(xojalik). Fayl boshidagi izoh nega shunday qilinganini aytadi (kanon): "App'ning useRef'i (17/#33; #28 da modul o'zgaruvchisi edi — ikkita App bo'lsa, bitta hisoblagichni bo'lishardi)". Bu «Modul o'zgaruvchisi — hamma uchun bitta» bo'limidagi A va B stollari muammosining vazifalar dagi shakli.
xojalik ning ichida ikki maydon bor — ikki alohida ref emas. Ular birga o'zgaradi va birga uzatiladi. Ref — istalgan qiymat uchun quti, jumladan obyekt uchun ham.
Diqqat: Bu qadam o'zi alohida commit emas. Tekshiruvlar va commit — useRef II darsi oxirida.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
taymer to'xtamaydi, timerId = undefined |
id let da, qayta chizishda yo'qoldi |
useRef |
| bir komponent boshqasining taymerini to'xtatdi | modul o'zgaruvchisi | useRef (har nusxaga alohida) |
| ref o'zgardi, ekran eski | ref qayta chizish chaqirmaydi | ekranda kerak — state |
Cannot access refs during render |
JSX yoki komponent tanasida ref.current |
ishlovchi yoki effektga ko'chiring |
| StrictMode'da hisoblagich ikki baravar | chizish paytida ref'ga yozilgan | effekt yoki ishlovchida yozing |
Expected 1 arguments, but got 0 (TS) |
React 19 turlarida useRef<T>() |
useRef<T>(undefined) yoki null |
9. Mashqlar
Mashqlar kurs/mashqlar/17/33-useref-xotira/ da. Har yechimni «Ishga tushir» bilan sinang.
1-mashq (oson): state yoki ref?
Har qiymat uchun tanlang: state yoki ref.
- Savatdagi taomlar soni (ekranda ko'rinadi):
setIntervalqaytargan id:- Yo'ldagi so'rovning
AbortControlleri: - Qidiruv maydonidagi matn (controlled input):
Yechim
Ekranda ko'rinadigan va o'zgarganda ekran yangilanishi kerak bo'lgan narsa — state (taomlar soni, maydon matni). Ekranga chiqmaydigan "xo'jalik" ma'lumoti — ref: taymer id'si, AbortController. Savol: "bu o'zgarsa, ekran o'zgarishi kerakmi?" Ha — state, yo'q — ref.
2-mashq (o'rta): bosishlar statistikasi
Jasur aka bilmoqchi: mehmon «Osh +» ni necha marta bosdi? Bu son ekranda ko'rinmasligi kerak — faqat «Hisobot» bosilganda konsolga chiqsin. Sardor uni state'da saqladi va har bosishda keraksiz chizish bo'lyapti (konsoldagi "chizildi" qatorlari). Ref bilan qayta yozing: "chizildi" faqat bir marta chiqsin. Ishora: «Ref o'zgaradi — ekran yo'q» bo'limi.
import { useState } from "react";
function App() {
const [clicks, setClicks] = useState(0);
console.log("chizildi");
const add = () => setClicks(clicks + 1);
return (
<div>
<button onClick={add}>Osh +</button>{" "}
<button onClick={() => console.log("bosishlar:", clicks)}>
Hisobot
</button>
</div>
);
}Konsolda:
chizildi
chizildi
chizildi
chizildi
bosishlar: 3Yechim
import { useRef } from "react";
function App() {
const clicks = useRef(0);
console.log("chizildi");
const report = () => console.log("bosishlar:", clicks.current);
return (
<div>
<button onClick={() => clicks.current++}>Osh +</button>{" "}
<button onClick={report}>Hisobot</button>
</div>
);
}Konsolda:
chizildi
bosishlar: 3To'rt chizish o'rniga bitta. clicks.current++ — ishlovchida, demak qoida buzilmadi. Agar keyinroq Jasur aka sonni ekranda ko'rmoqchi bo'lsa — state'ga qaytish kerak: ref ekranni yangilamaydi.
3-mashq (qiyin): ikki stolning eslatmasi
«Modul o'zgaruvchisi — hamma uchun bitta» bo'limidagi Reminder ni tuzating: har stolning o'z taymeri bo'lsin. Blok: A va B eslatmani yoqadi, A bekor qiladi. Natijada faqat Choy tayyor! (B) chiqishi kerak. Qo'shimcha talab: eslatma yoqilganda eski taymer bo'lsa — avval uni to'xtating (ikki marta bosilsa, ikki taymer bo'lmasin). Ishora: clearTimeout(undefined) yoki clearTimeout(null) xavfsiz.
Yechim
import { useRef, useState } from "react";
function Reminder({ table }) {
const [waiting, setWaiting] = useState(false);
const timerRef = useRef(null); // har Reminder'ning o'z qutisi
function remind() {
clearTimeout(timerRef.current); // eski taymer bo'lsa — to'xtat
const done = () => {
console.log(`Choy tayyor! (${table})`);
setWaiting(false);
};
timerRef.current = setTimeout(done, 300);
setWaiting(true);
}
function cancel() {
clearTimeout(timerRef.current);
setWaiting(false);
}
return (
<p>
{table}: {waiting ? "kutilmoqda" : "—"}{" "}
<button onClick={remind}>Eslatma {table}</button>{" "}
<button onClick={cancel}>Bekor {table}</button>
</p>
);
}
function App() {
return (
<div>
<Reminder table="A" />
<Reminder table="B" />
</div>
);
}Konsolda:
Choy tayyor! (B)Ekranda:
A: — Eslatma A Bekor A
B: — Eslatma B Bekor BHar Reminder o'z useRef qutisini oldi — A va B bir-birining taymeriga tegmaydi. remind boshidagi clearTimeout "ikki marta bosish" holatini yopadi. Taymer tugaganda setWaiting(false) — B qatori ham "—" ga qaytdi. Bitta kamchilik qoldi: komponent ekrandan olib tashlansa, yo'ldagi taymer ishlayveradi. Buni effekt cleanup'i bilan yopish — 4-mashqdan keyingi o'zingizning qadamingiz bo'lsin (useEffect III).
4-mashq: Vazifalar qadami
vazifalar da refactor/reflar branch'ini oching. elonTaymeri va server-sinxron.ts dagi ikki modul o'zgaruvchisini «Vazifalar qadami: modul o'zgaruvchilari ref'larga» bo'limidagidek App ning ref'lariga ko'chiring.
Yechim
Tekshiruv ro'yxati:
grep -n "^let " src/App.tsx src/server-sinxron.ts— bo'sh (modul darajasidaletqolmadi).npm run tip—sinxronniTox,sinxronla,yozuvniYuborchaqiruvlaridaxojalikuzatilgan (Sozlagichlarturida endixojalikmaydoni bor — unutilsa, tsc aytadi).npm run lint—react-hooks/refsxatosi yo'q: ref'lar faqat ishlovchi va effektlarda.- Commit hali yo'q — useRef II darsida.
10. Real ishda
- Taymerlar,
AbortController, WebSocket ulanishi, uchinchi tomon kutubxonasining nusxasi (xarita, pleyer, grafik) — ref'larda saqlanadigan odatiy narsalar. - Debounce va throttle (Debounce va throttle) React'da ko'pincha ref ichidagi taymer bilan yoziladi; tayyor hooklar ham shunday (Custom hooklar II).
- Kod ko'rigida: komponent faylida modul darajasidagi
let— deyarli har doim xato belgisi. Savol: "ikkita nusxa bo'lsa nima bo'ladi?" - Intervyu savollari: "useRef va useState farqi?", "nega ref o'zgarganda komponent qayta chizilmaydi?", "ref'ni render paytida o'qish nega yomon?".
Xulosa
useRef(boshlang'ich)—{ current }qutisi: har chizishda o'sha obyekt, o'zgarsa qayta chizish yo'q.lethar chizishda yo'qoladi, modul o'zgaruvchisi hamma nusxaga bitta, state ortiqcha chizadi — ref ekranga chiqmaydigan xotira uchun.ref.current— faqat ishlovchi va effektlarda; chizish paytida emas (ESLintreact-hooks/refs). Istisno — bir martalik lazy init (dangasa boshlash).- Oldingi qiymat: effektda kerak — ref; ekranda kerak — state.
- TypeScript:
useRef<ReturnType<typeof setTimeout>>(undefined)— React 19 da boshlang'ich qiymat majburiy. vazifalar:elonTaymeriva sinxron xo'jaligi (pult,kutilmoqda) modul o'zgaruvchisidanAppning ref'lariga ko'chdi.
Keyingi dars: useRef II: DOM elementlari va ref prop (React 19) — ref'ning ikkinchi vazifasi: DOM elementiga havola, fokus, ref callback va React 19 dagi ref prop.
Manbalar
- React: "Referencing Values with Refs" — react.dev/learn/referencing-values-with-refs
- React:
useRef— "Do not write or read ref.current during rendering", "Avoiding recreating the ref contents" — react.dev/reference/react/useRef - React:
useState— "Storing information from previous renders" — react.dev/reference/react/useState
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!