Mundarija (31)
- Bu darsda
- 1. Nega bu kerak?
- 2. DOM elementiga ref
- 2.1 Fokus tugmasi
- 2.2 Ref qachon to'ladi
- 2.3 Ochilganda fokus — ref + effekt
- 2.4 Boshqa DOM ishlari
- 3. Ref callback
- 3.1 Funksiya ham ref
- 3.2 TypeScript'dagi tuzoq
- 4. Ro'yxat uchun ref Map
- 4.1 Muammo
- 5. ref — oddiy prop (React 19)
- 5.1 O'z komponentingizga ref
- 5.2 Eski usul: forwardRef
- 5.3 TypeScript bilan
- 6. Vazifalar qadami: fokus.ts o'rniga ref'lar
- 6.1 Nima o'zgaradi
- 6.2 Tugmalar — ref Map va React 19 cleanup
- 6.3 Dialog va AmalTugmasi — ref prop
- 6.4 React Compiler qoidasi topgan tartib
- 6.5 Commit va tekshiruvlar
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson): qachon null?
- 2-mashq (o'rta): xatoda fokus
- 3-mashq (qiyin): o'chirgandan keyin fokus
- 4-mashq: Vazifalar qadami
- 9. Real ishda
- Xulosa
- Manbalar
useRef II: DOM elementiga ref, fokus va React 19 dagi ref prop
Qisqacha:
<input ref={inputRef} />yozsangiz, React commit paytida haqiqiy DOM elementiniinputRef.currentga qo'yadi va element olib tashlangandanullqiladi. Shunda React bajarmaydigan DOM ishlarini qilasiz:focus(),select(),scrollIntoView(),showModal(), o'lcham olish. Ref chizish paytida halinull— undan ishlovchi va effektlarda foydalaniladi.refga funksiya ham berish mumkin (ref callback). React 19 da u cleanup qaytara oladi — ro'yxat uchun ref Map shu bilan toza yoziladi. React 19 daref— funksiya komponent uchun oddiy prop:function Input({ ref }). EskiforwardRefendi shart emas.
Bu darsda
- DOM elementiga ref ulab, fokus va matn belgilashni boshqarasiz.
- Ref qachon
nullva qachon element ekanini Render → Commit → Effekt tartibida ko'rsatasiz. - Ref callback yozasiz va React 19 dagi ref cleanup'ni ishlatasiz.
- O'z komponentingizdan
refprop orqali ichki elementni ochib berasiz (forwardRefsiz). - Ro'yxatdagi ko'p elementlar uchun ref Map tuzasiz va
vazifalardagigetElementByIdlardan qutulasiz.
Oldin bilishingiz kerak: useRef I, useEffect I, Fokus va tahrir atributlari, <dialog> va Popover API JS'dan.
1. Nega bu kerak?
React nima darsida qoida qo'ygan edik: React boshqaradigan joyda DOM'ni qo'lda o'zgartirmang. Lekin ba'zi ishlarni React umuman qilmaydi — ular holat emas, harakat. "Fokusni shu maydonga qo'y", "shu qatorga aylantir", "dialogni ochiq holda ko'rsat", "maydon kengligini o'lcha". JSX'da focused={true} degan atribut yo'q.
Bobur «Bahor» sahifasini faqat klaviatura bilan ishlatadi. U "Tahrirlash" ni bosganda fokus tahrir maydoniga o'tishi kerak — aks holda u Tab'ni o'n marta bosib, maydonni qidiradi. Vazifa o'chirilganda fokus qo'shni qatorga o'tishi kerak — aks holda u sahifa boshiga "uloqtiriladi". Malika (ekran o'quvchi) uchun ham shunday.
vazifalar da bu ishlar hozir fokus.ts faylida: document.getElementById(...) va closest("li") bilan. Kanondagi izohlar o'zi aytadi: "Ref'lar 17/#34 da — hozircha id bilan". Bugun — o'sha #34.
Hayotiy o'xshatish: mehmonxonada mehmonga xona kaliti beriladi. Kalit — xonaning o'zi emas, lekin u bilan xonaga kirib, chiroqni yoqish mumkin. Ref — DOM elementining kaliti: React elementni quradi va kalitni sizning qutingizga (ref.current) qo'yadi.
2. DOM elementiga ref
2.1 Fokus tugmasi
useRef I darsidagi qutini endi ref atributiga beramiz:
import { useRef } from "react";
function App() {
const inputRef = useRef(null);
function startNote() {
inputRef.current.focus(); // DOM metodi
console.log("fokusda:", document.activeElement.id);
}
return (
<div>
<input id="izoh" ref={inputRef} placeholder="Izoh" />
<button onClick={startNote}>Izoh yozish</button>
</div>
);
}Konsolda:
fokusda: izohUch qadam:
useRef(null)— bo'sh quti. DOM ref'i uchun boshlang'ich qiymat doimnull.ref={inputRef}— React'ga: "shu<input>ning DOM elementini qutiga qo'y".- Ishlovchida
inputRef.current— haqiqiyHTMLInputElement, unda DOM'dan tanish hamma metod bor:focus(),select(),value,getBoundingClientRect().
ref — maxsus atribut: u DOM'ga yozilmaydi va props sifatida emas, React'ning o'zi tomonidan ishlatiladi (xuddi key kabi).
2.2 Ref qachon to'ladi
Ref chizish paytida emas, commit paytida to'ladi — React elementni DOM'ga qo'ygan zahoti:
import { useEffect, useRef } from "react";
function App() {
const inputRef = useRef(null);
console.log("render:", inputRef.current);
useEffect(() => {
console.log("effekt:", inputRef.current.tagName);
}, []);
return <input ref={inputRef} />;
}Konsolda:
render: null
effekt: INPUT| Bosqich | inputRef.current |
Ishlatsa bo'ladimi |
|---|---|---|
| Render (birinchi) | null |
element hali yo'q |
| Commit | React elementni qo'yadi | — |
| Effekt | <input> |
|
| Hodisa ishlovchisi | <input> |
|
| Cleanup (olib tashlanganda) | React null qiladi |
— |
sequenceDiagram
participant K as Komponent
participant R as React DOM
participant Q as inputRef.current
K->>R: Render: JSX qaytadi (Q = null)
R->>R: Commit: input DOM'ga qo'yildi
R->>Q: Q = input elementi
R->>K: Effekt: Q.focus() mumkin
Note over R: element olib tashlandi
R->>Q: Q = nullDiagrammada ref'ni React'ning o'zi to'ldiradi va bo'shatadi — siz faqat o'qiysiz. Shu sababli DOM ref'i bilan ishlash joyi — effekt va ishlovchilar. Chizish paytida ref'ni o'qimaslik qoidasi (useRef I) bu yerda ikki karra to'g'ri: birinchi chizishda u hatto null.
2.3 Ochilganda fokus — ref + effekt
"Tahrir qatori paydo bo'lganda — fokus maydonda, matn belgilangan" — bu chizish natijasi. Demak effekt (useEffect I). Effekt DOM elementi bilan sinxronlaydi — tashqi tizim:
import { useEffect, useRef, useState } from "react";
function EditRow({ text }) {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
inputRef.current.select(); // yozish eski matnni almashtiradi
}, []);
return (
<input ref={inputRef} defaultValue={text} aria-label="Matn" />
);
}
function App() {
const [editing, setEditing] = useState(false);
const where = () => console.log(document.activeElement.tagName);
return (
<div>
{editing ? (
<EditRow text="Non olish" />
) : (
<button onClick={() => setEditing(true)}>Tahrirlash</button>
)}
<button onClick={where}>Fokus qayerda?</button>
</div>
);
}Blokni ishga tushiring: «Tahrirlash» dan keyin kursor maydonda, "Non olish" belgilangan. Bobur darhol yangi matn yozishi mumkin. Massiv [] — faqat qator paydo bo'lganda. Agar qator qayta chizilsa (masalan, ota yangilansa), belgilash buzilmaydi.
Maslahat: HTML'da
autoFocusatributi ham bor (<input autoFocus />) va React uni qo'llab-quvvatlaydi. U sahifa ochilganda ishlaydi va matnni belgilamaydi. Shartli paydo bo'ladigan maydon vaselect()uchun ref + effekt aniqroq.
2.4 Boshqa DOM ishlari
Fokusdan tashqari, ref bilan qilinadigan odatiy ishlar:
| Vazifa | DOM metodi | Qayerda |
|---|---|---|
| matnni belgilash | select() |
ishlovchi / effekt |
| elementga aylantirish | scrollIntoView({ block: "nearest" }) |
ishlovchi |
| modal dialog | showModal(), close() |
ishlovchi |
| o'lcham | getBoundingClientRect() |
effekt (useLayoutEffect) |
| video | play(), pause() |
ishlovchi |
Aylantirish va dialog sayt oynasidagi haqiqiy brauzerda ishlaydi:
import { useRef } from "react";
const MENU = ["Osh", "Lag'mon", "Manti", "Somsa", "Chuchvara",
"Norin", "Shashlik", "Ko'k choy"];
function App() {
const lastRef = useRef(null);
const dialogRef = useRef(null);
return (
<div>
<button onClick={() => lastRef.current.scrollIntoView()}>
Oxirgi taomga
</button>{" "}
<button onClick={() => dialogRef.current.showModal()}>
Bron oynasi
</button>
<ul style={{ height: 80, overflow: "auto" }}>
{MENU.map((d, i) => (
<li key={d} ref={i === MENU.length - 1 ? lastRef : null}>
{d}
</li>
))}
</ul>
<dialog ref={dialogRef}>
<p>Stol bron qilindi.</p>
<button onClick={() => dialogRef.current.close()}>
Yopish
</button>
</dialog>
</div>
);
}«Ishga tushir» dan keyin «Oxirgi taomga» ni bosing — ro'yxat "Ko'k choy" gacha aylanadi. «Bron oynasi» — <dialog> modal ochiladi, fon bosilmaydi (dialog va Popover API). E'tibor bering: dialogning ochiq yoki yopiqligini React emas, brauzer boshqaradi — biz faqat buyruq beramiz.
Diqqat: Ref orqali o'qing va buyruq bering, lekin React chizgan DOM'ni o'zgartirmang:
ref.current.remove(),appendChild,textContent = …— React keyingi chizishda o'z daraxtiga ishonadi va xato berishi yoki o'zgarishingizni o'chirib yuborishi mumkin. Matn va tuzilma — state orqali.
Tekshirib ko'ring: Nega
<input ref={inputRef} />dan keyin darhol, komponent tanasidainputRef.current.focus()yozib bo'lmaydi?
Javob
Komponent tanasi — chizish. Birinchi chizishda <input> hali DOM'da yo'q, inputRef.current — null: TypeError: Cannot read properties of null (reading 'focus'). Bundan tashqari, focus() — yon ta'sir, chizish esa toza bo'lishi kerak. To'g'ri joy — effekt (paydo bo'lganda) yoki ishlovchi (bosilganda).
3. Ref callback
3.1 Funksiya ham ref
ref ga obyekt o'rniga funksiya berish mumkin. React uni element DOM'ga qo'yilganda element bilan chaqiradi. Ref callback (ref callback) — ref={(el) => …} shaklidagi funksiya: element ulanganda chaqiriladi.
React 19 da ref callback cleanup funksiyasi qaytara oladi — element olib tashlanganda React aynan uni chaqiradi (effekt cleanup'i kabi). StrictMode bilan:
import { StrictMode, useState } from "react";
import { createRoot } from "react-dom/client";
function App() {
const [show, setShow] = useState(true);
return (
<div>
{show && (
<input
ref={(el) => {
console.log("ulandi:", el.tagName);
return () => console.log("uzildi");
}}
/>
)}
<button onClick={() => setShow(false)}>Yashir</button>
</div>
);
}
createRoot(document.getElementById("root")).render(
<StrictMode>
<App />
</StrictMode>,
);Konsolda:
ulandi: INPUT
uzildi
ulandi: INPUT
uzildiDastlabki uch qator — StrictMode sinovi: effektlar kabi ref callback ham dev'da ulandi → uzildi → ulandi qilinadi (bu React 19 da qo'shildi). To'rtinchisi — «Yashir»: <input> olib tashlandi, cleanup ishladi.
React 19 dan oldin cleanup yo'q edi: element olib tashlanganda React callback'ni null bilan qayta chaqirardi. Eski kodda if (el === null) { … } tekshiruvini ko'rasiz. React 19 cleanup qaytarilmasa, hali ham shunday qiladi — shuning uchun kanondagi tugmaRef boshida null ni tekshiradi.
3.2 TypeScript'dagi tuzoq
Cleanup'ning qo'shilishi bitta qadimgi odatni buzdi. Qisqa arrow funksiya qiymat qaytaradi:
let current: HTMLInputElement | null = null;
export function App() {
return <input ref={(el) => (current = el)} />;
}Xato:
TS2322: Type '(el: HTMLInputElement | null) => HTMLInputElement | null' is not assignable to type 'Ref<HTMLInputElement> | undefined'."(el) => HTMLInputElement | null turidagi funksiyani Ref<HTMLInputElement> ga berib bo'lmaydi." Qavs ichidagi tenglik qiymat qaytaradi (el). React 19 turlari esa ref callback'dan faqat cleanup funksiyasi yoki hech narsa kutadi. Tuzatish — jingalak qavs: ref={(el) => { current = el; }}.
4. Ro'yxat uchun ref Map
4.1 Muammo
useRef — hook, uni sikl ichida chaqirib bo'lmaydi (Hooklar qoidalari). Ro'yxatdagi har qatorga alohida ref kerak bo'lsa-chi? Masalan, vazifa o'chirilganda fokus qo'shni qatorning tugmasiga o'tishi kerak — qaysi qator, oldindan noma'lum.
Yechim — bitta ref, ichida Map (Map): kalit — id, qiymat — element. Ref callback element ulanganda set, cleanup'da delete qiladi:
import { useRef } from "react";
const DISHES = [
{ id: 1, name: "Osh" },
{ id: 2, name: "Lag'mon" },
{ id: 3, name: "Manti" },
];
function App() {
const buttons = useRef(new Map()); // id → <button>
function focusDish(id) {
buttons.current.get(id)?.focus();
console.log("fokusda:", document.activeElement.textContent);
console.log("Map'da:", buttons.current.size, "ta");
}
return (
<div>
<ul>
{DISHES.map((d) => (
<li key={d.id}>
<button
ref={(el) => {
buttons.current.set(d.id, el);
return () => buttons.current.delete(d.id);
}}
>
{d.name}
</button>
</li>
))}
</ul>
<button onClick={() => focusDish(2)}>Lag'mon'ga o't</button>
</div>
);
}Konsolda:
fokusda: Lag'mon
Map'da: 3 taEkranda:
Osh
Lag'mon
Manti
Lag'mon'ga o'tHar <li> o'z ref callback'ini oladi — u d.id ni closure orqali biladi. Qator olib tashlansa, cleanup uni Map'dan o'chiradi. Map'da eskirgan, DOM'da yo'q element qolmaydi. Cleanup bo'lmaganda Map o'chirilgan qatorlarning elementlarini abadiy saqlab qolardi (xotira oqishi).
Tekshirib ko'ring:
useRef(new Map())—new Map()har chizishda yaratiladimi? Bu muammomi?
Javob
Ha, new Map() ifodasi har chizishda bajariladi, lekin useRef faqat birinchisini oladi — qolganlari tashlanadi. Kichik Map uchun bu sezilmaydi. Qimmat obyekt bo'lsa — useRef I darsidagi lazy init (dangasa boshlash) (if (ref.current === null) ref.current = …).
5. ref — oddiy prop (React 19)
5.1 O'z komponentingizga ref
Siz SearchInput komponenti yasadingiz — ichida <input> va yorliq. Ota undagi maydonga fokus bermoqchi: <SearchInput ref={inputRef} />. React 19 da ref funksiya komponent uchun oddiy prop — uni boshqa props kabi olib, ichki elementga uzatasiz:
import { useRef } from "react";
function SearchInput({ ref, label }) {
return (
<label>
{label} <input ref={ref} />
</label>
);
}
function App() {
const inputRef = useRef(null);
function search() {
inputRef.current.focus();
console.log("fokusda:", document.activeElement.tagName);
}
return (
<div>
<SearchInput ref={inputRef} label="Taom nomi" />
<button onClick={search}>Qidirish</button>
</div>
);
}Konsolda:
fokusda: INPUTOta SearchInput ning ichini bilmaydi. U faqat "menga sening maydoningning ref'ini ber" deydi, komponent esa qaysi elementni berishni o'zi hal qiladi.
5.2 Eski usul: forwardRef
React 18 va undan oldin funksiya komponent ref ni props'da olmasdi. React uni "yutib yuborardi" va ogohlantirardi. Buning uchun maxsus o'ram bor edi — forwardRef:
import { forwardRef, useEffect, useRef } from "react";
// Eski kodda shunday yozilgan (React 18 uslubi)
const OldInput = forwardRef(function OldInput(props, ref) {
return <input ref={ref} {...props} />;
});
function App() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
console.log("fokusda:", document.activeElement.placeholder);
}, []);
return <OldInput ref={inputRef} placeholder="Ism" />;
}Konsolda:
fokusda: IsmReact 19.3 da forwardRef hali ishlaydi va ogohlantirish bermaydi. React hujjati esa uni "kelajakda eskiradi" deb belgilagan va yangi kodda ref prop'ni tavsiya qiladi. Eski loyihalarda, UI kutubxonalarida (Radix, MUI'ning eski versiyalari) forwardRef ni ko'p uchratasiz — endi uni o'qiy olasiz.
Ota komponent bolaning DOM elementini emas, faqat tanlangan metodlarini ko'rishi kerak bo'lsa (open(), reset()) — buning uchun useImperativeHandle bor (useImperativeHandle darsida).
5.3 TypeScript bilan
useRef<HTMLInputElement>(null) ning turi — RefObject<HTMLInputElement | null>. Prop sifatida ref ning turi — Ref<HTMLInputElement> (u obyekt ref'ini ham, callback'ni ham qamraydi):
import { useRef } from "react";
import type { Ref } from "react";
type Props = {
ref?: Ref<HTMLInputElement>; // React 19: ref — oddiy prop
label: string;
};
function SearchInput({ ref, label }: Props) {
return <input ref={ref} aria-label={label} />;
}
export default function App() {
const inputRef = useRef<HTMLInputElement>(null);
return (
<div>
<SearchInput ref={inputRef} label="Qidiruv" />
<button onClick={() => inputRef.current?.focus()}>Fokus</button>
</div>
);
}Ekranda:
FokusinputRef.current?.focus() — ?. shart: tur HTMLInputElement | null, TypeScript null bo'lishi mumkinligini biladi (React + TS II darsida hooklar turlari batafsil).
6. Vazifalar qadami: fokus.ts o'rniga ref'lar
6.1 Nima o'zgaradi
refactor/reflar branch'ida useRef I darsidagi xo'jalik ref'lari tayyor. Bugun — DOM ref'lari va commit. fokus.ts (81 qator, getElementById va closest bilan) butunlay o'chadi:
| Joy | Holat 31 | Holat 34 |
|---|---|---|
| qator tugmalari (fokus) | getElementById + [data-amal] |
ref Map: tugmaRef(id, amal) |
| tahrir maydoni | tahrirgaOt() — id bilan |
TahrirQatori: ref + effekt |
| o'chirish dialogi | dialogniOch() — id bilan |
OchirishDialog ga ref prop |
| eksport matni | getElementById("eksport-matn") |
useRef<HTMLTextAreaElement> |
| qo'shni qator | DOM'dagi nextElementSibling |
qoshniId(korinadigan, id) — sof funksiya |
6.2 Tugmalar — ref Map va React 19 cleanup
Kanondagi ref callback yasovchi (aynan):
// Ref callback: React elementni qo'yganda chaqiradi, qaytargan
// funksiyasini — element olib tashlanganda (React 19: ref cleanup)
function tugmaRef(id: number, amal: FokusAmali) {
return (element: HTMLElement | null) => {
if (element === null) {
return undefined;
}
const kalit = `${id}:${amal}`;
tugmalar.current.set(kalit, element);
return () => {
tugmalar.current.delete(kalit);
};
};
}«Ro'yxat uchun ref Map» dagi g'oya: tugmalar — useRef(new Map<string, HTMLElement>()), kalit — "5:ochir" kabi. tugmaRef(id, amal) ref callback qaytaradi — har qatorda ref={tugmaRef(vazifa.id, "almashtir")} deb ishlatiladi. Fokus berish endi DOM qidiruvi emas, Map'dan olish:
(
topilgan(id) ??
topilgan(zaxiraId) ??
sarlavhaRef.current
)?.focus();Avval o'sha qator, bo'lmasa qo'shnisi, u ham bo'lmasa — "Ro'yxat" sarlavhasi (sarlavhaRef). Bobur hech qachon sahifa boshiga uloqtirilmaydi.
6.3 Dialog va AmalTugmasi — ref prop
OchirishDialog va AmalTugmasi — o'z komponentlarimiz. Ota ularning ichidagi <dialog> va <button> ga ref uzatadi — React 19 usulida (kanon):
type Props = {
+ ref: Ref<HTMLDialogElement>; // React 19: ref — oddiy prop
matn: string | null; // o'chirilayotgan vazifa matni
onJavob: (tasdiq: boolean) => void;
};
-export function OchirishDialog({ matn, onJavob }: Props) {
+export function OchirishDialog({ ref, matn, onJavob }: Props) {
return (
<dialog
+ ref={ref}App da dialog dialogRef.current.showModal() bilan ochiladi — fokus.ts dagi dialogniOch() o'rniga. forwardRef hech qayerda yo'q.
6.4 React Compiler qoidasi topgan tartib
fondaYangila (boshqa tabdan kelgan yangilanishda fokusni saqlash) endi App ichidagi funksiya bo'ldi — ref'larni ishlatadi. Uni effekt hodisasidan keyin e'lon qilganimizda ESLint (plagin 7, React Compiler qoidalari) to'xtatdi (haqiqiy):
`fondaYangila` accessed before it is declared react-hooks/immutability"fondaYangila e'lon qilinishidan oldin murojaat qilingan." Funksiya e'loni ko'tariladi (hoisting — Funksiya e'lon qilish va chaqirish) va JavaScript'da bu ishlardi. Lekin kompilyator qoidasi komponent ichida yuqoridan pastga tartibni talab qiladi. Funksiya effekt hodisasidan oldinga ko'chirildi.
6.5 Commit va tekshiruvlar
Commit (kanon): refactor: taymer va sinxron xo'jaligi useRef, fokus — ref Map. Body: elonTaymeri, { pult, kutilmoqda } — useRef; fokus.ts o'chirildi: tugmalar — ref Map (tugmaRef, ref cleanup), dialog — ref prop, TahrirQatori fokus+belgilash — ref + effekt; qoshniId — sof (holat.ts).
npm test— node:test 148 (qoshniIduchun +1), Vitest 23. Lint, format, tip —exit 0.- Brauzer tekshiruvi — 159/159. Undagi klaviatura ssenariylari (o'chirgandan keyin fokus qo'shni qatorga, Esc bilan tahrirdan chiqish) — o'sha natija, endi ref'lar bilan.
getElementByIdfaqatmain.tsxda qoldi — React ildizini topish uchun.- Diff: 14 fayl, +266 −201. Build:
index-…js248.58 kB (gzip 78.86).
7. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Cannot read properties of null (reading 'focus') |
ref chizish paytida yoki element hali yo'q | effekt yoki ishlovchida; ?. |
| fokus kutilgan joyda emas | focus() state yangilanishidan oldin chaqirildi |
effekt yoki flushSync dan keyin |
TS2322: … is not assignable to type 'Ref<…>' |
ref callback qiymat qaytardi | { … } jingalak qavs |
| Map'da o'chgan elementlar | ref callback cleanup'siz | return () => map.delete(id) |
ota'dagi ref null (React 18 kodi) |
funksiya komponent ref ni uzatmadi |
React 19: ref prop; 18: forwardRef |
React chizgan elementni remove() qildingiz |
DOM'ni qo'lda o'zgartirish | state orqali |
8. Mashqlar
Mashqlar kurs/mashqlar/17/34-useref-dom/ da. Har yechimni «Ishga tushir» bilan sinang.
1-mashq (oson): qachon null?
Kodni ishga tushirmasdan, konsol qatorlarini to'ldiring.
import { useEffect, useRef } from "react";
function App() {
const ref = useRef(null);
console.log("A:", ref.current === null);
useEffect(() => {
console.log("B:", ref.current === null);
}, []);
const check = () => console.log("C:", ref.current.tagName);
return (
<button ref={ref} onClick={check}>
Tekshir
</button>
);
}- A qatori:
- B qatori:
- C qatori:
Yechim
Konsolda:
A: true
B: false
C: BUTTONA — birinchi chizish, element hali yo'q. B — effekt commit'dan keyin, ref to'lgan. C — bosish, element joyida.
2-mashq (o'rta): xatoda fokus
Bron formasi: ism bo'sh yuborilsa, xato matni chiqadi — lekin fokus tugmada qoladi. Bobur qayerga qaytishni bilmaydi. Ref bilan tuzating: xato bo'lsa, fokus ism maydoniga o'tsin. Konsolga fokusdagi element id sini yozing. Ishora: «Fokus tugmasi» bo'limi; fokus setError dan keyin, o'sha ishlovchida.
import { useState } from "react";
function App() {
const [error, setError] = useState("");
function submit(e) {
e.preventDefault();
const name = new FormData(e.currentTarget).get("name");
if (name.trim() === "") {
setError("Ismni kiriting");
}
console.log("fokusda:", document.activeElement.id);
}
return (
<form onSubmit={submit}>
<input id="ism" name="name" aria-label="Ism" />
<p role="alert">{error}</p>
<button id="bron">Bron qilish</button>
</form>
);
}Yechim
import { useRef, useState } from "react";
function App() {
const [error, setError] = useState("");
const nameRef = useRef(null);
function submit(e) {
e.preventDefault();
const name = new FormData(e.currentTarget).get("name");
if (name.trim() === "") {
setError("Ismni kiriting");
nameRef.current.focus(); // ✅ xato joyiga
}
console.log("fokusda:", document.activeElement.id);
}
return (
<form onSubmit={submit}>
<input id="ism" ref={nameRef} name="name" aria-label="Ism" />
<p role="alert">{error}</p>
<button id="bron">Bron qilish</button>
</form>
);
}Konsolda:
fokusda: ismEkranda:
Ismni kiriting
Bron qilishFokus maydonda — Bobur darhol yozishi, Malika esa role="alert" tufayli xatoni eshitishi mumkin. vazifalar dagi YangiVazifaForma aynan shunday qiladi (maydonRef).
3-mashq (qiyin): o'chirgandan keyin fokus
Ro'yxatdagi har taomning «O'chirish» tugmasi bor. Taom o'chirilganda fokus keyingi taomning «O'chirish» tugmasiga o'tishi kerak (oxirgisi o'chsa — oldingisiga). Hozir fokus yo'qolib, BODY ga tushadi. Ref Map va flushSync bilan tuzating:
buttons—useRef(new Map()), ref callback cleanup bilan;- o'chirishdan oldin qo'shni id'ni hisoblang;
flushSync(() => setDishes(…))— DOM darhol yangilansin (State II: flushSync —vazifalarda ko'p ishlatilgan), keyin fokus.
Konsolga fokusdagi tugmaning aria-label ini yozing.
import { useState } from "react";
const START = [
{ id: 1, name: "Osh" },
{ id: 2, name: "Lag'mon" },
{ id: 3, name: "Manti" },
];
function App() {
const [dishes, setDishes] = useState(START);
function remove(id) {
setDishes(dishes.filter((d) => d.id !== id));
console.log("fokusda:", document.activeElement.tagName);
}
return (
<ul>
{dishes.map((d) => (
<li key={d.id}>
{d.name}{" "}
<button
aria-label={`O'chirish: ${d.name}`}
onClick={() => remove(d.id)}
>
O'chirish
</button>
</li>
))}
</ul>
);
}Yechim
import { useRef, useState } from "react";
import { flushSync } from "react-dom";
const START = [
{ id: 1, name: "Osh" },
{ id: 2, name: "Lag'mon" },
{ id: 3, name: "Manti" },
];
function App() {
const [dishes, setDishes] = useState(START);
const buttons = useRef(new Map()); // id → <button>
function remove(id) {
const i = dishes.findIndex((d) => d.id === id);
const next = dishes[i + 1] ?? dishes[i - 1]; // qo'shni yoki yo'q
flushSync(() => setDishes(dishes.filter((d) => d.id !== id)));
if (next) buttons.current.get(next.id)?.focus();
const label = document.activeElement.getAttribute("aria-label");
console.log("fokusda:", label);
}
return (
<ul>
{dishes.map((d) => (
<li key={d.id}>
{d.name}{" "}
<button
ref={(el) => {
buttons.current.set(d.id, el);
return () => buttons.current.delete(d.id);
}}
aria-label={`O'chirish: ${d.name}`}
onClick={() => remove(d.id)}
>
O'chirish
</button>
</li>
))}
</ul>
);
}Konsolda:
fokusda: O'chirish: MantiEkranda:
Osh O'chirish
Manti O'chirishQo'shni o'chirishdan oldin hisoblandi — keyin u ro'yxatda bo'lmasdi. flushSync dan keyin DOM yangilangan: Lag'mon qatori yo'q va uning ref cleanup'i Map'dan o'chirgan. Manti tugmasi esa joyida. Bu vazifalar dagi fokusla(zaxira, "ochir", null) ning kichik nusxasi.
4-mashq: Vazifalar qadami
refactor/reflar branch'ida «Vazifalar qadami: fokus.ts o'rniga ref'lar» bo'limidagi o'zgarishlarni kiriting, fokus.ts ni o'chiring, commit qiling va PR oching.
Yechim
Tekshiruv ro'yxati:
grep -rn "getElementById" src/— faqatmain.tsx(ildiz).grep -rn "forwardRef" src/— bo'sh:ref— oddiy prop.npm run lint—react-hooks/immutabilityyokireact-hooks/refsxatosi yo'q.- Klaviatura bilan sinang: vazifani o'chiring — fokus qo'shni qatorning «O'chirish» tugmasida; tahrirlashni boshlang — matn belgilangan; Esc — fokus «Tahrirlash» ga qaytadi.
npm test— node 148, Vitest 23; brauzer tekshiruvi — 159/159.
Commit — refactor: taymer va sinxron xo'jaligi useRef, fokus — ref Map. PR'ni birlashtiring.
9. Real ishda
- Fokus boshqaruvi — accessibility'ning asosi: modal ochilganda fokus ichiga, yopilganda tugmaga qaytishi; xatoda — maydonga (Fokus boshqaruvi va klaviatura navigatsiyasi). Ref'siz buni toza yozib bo'lmaydi.
- Uchinchi tomon kutubxonalari (Leaflet xaritasi, Chart.js, video pleyer) DOM elementini talab qiladi — kutubxona nusxasi effekt ichida yaratiladi (masalan, Leaflet'da
L.map(ref.current)),remove()yokidestroy()— cleanup'da (Xaritalar I). - UI kutubxonalari (shadcn/ui, Radix) React 19 ga o'tishda
forwardRefni olib tashlab,refprop'ga o'tmoqda. Eski versiyalarda uni ko'p ko'rasiz. - Intervyu savollari: "DOM elementiga qanday murojaat qilinadi?", "ref callback nima va React 19 da nima o'zgardi?", "
forwardRefnima uchun kerak edi va hozir-chi?".
Xulosa
ref={myRef}— React commit paytida DOM elementinimyRef.currentga qo'yadi, olib tashlangandanullqiladi; chizish paytida u halinull.- Ref bilan — React bajarmaydigan buyruqlar:
focus(),select(),scrollIntoView(),showModal(), o'lcham. DOM tuzilmasini qo'lda o'zgartirmang. - Ref callback —
ref={(el) => …}; React 19 da u cleanup qaytaradi (StrictMode uni ham ikki marta sinaydi). TS'da qiymat qaytarish —TS2322. - Ro'yxat uchun — bitta ref ichida
Map, set va cleanup'da delete. - React 19:
ref— funksiya komponent uchun oddiy prop;forwardRef— eski kodda. vazifalar:fokus.tso'chdi — ref Map,refprop, ref + effekt; 159/159.
Keyingi dars: useImperativeHandle va imperativ API — komponent ota'ga DOM elementini emas, faqat tanlangan metodlarni (open, reset) ochib berishi.
Manbalar
- React: "Manipulating the DOM with Refs" — react.dev/learn/manipulating-the-dom-with-refs
- React:
refcallback va cleanup — react.dev/reference/react-dom/components/common#ref-callback - React blogi: "React v19" (2024-12-05) — "ref as a prop", "Cleanup functions for refs" — react.dev/blog/2024/12/05/react-19
- React:
forwardRef— react.dev/reference/react/forwardRef
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!