Mundarija (29)
- Bu darsda
- 1. Nega bu kerak?
- 2. Imperativ va deklarativ: eslatma
- 2.1 Brauzerdagi buyruqlar
- 2.2 ref prop bilan butun element ketadi
- 3. useImperativeHandle sintaksisi
- 3.1 Birinchi misol
- 3.2 Handle qachon paydo bo'ladi
- 4. Handle — faqat DOM emas
- 4.1 Bir nechta element va state
- 4.2 Handle stateni o'qisa: eskirgan qiymat
- 5. TypeScript'da handle turi
- 5.1 Ref<T> — handle turi
- 5.2 TypeScript ortiqcha so'rovni ushlaydi
- 6. Eski kod: forwardRef
- 7. Qachon props afzalroq
- 7.1 Imperativ API — oxirgi chora
- 7.2 Qaror jadvali
- 8. Ko'p uchraydigan xatolar
- 8.1 Render ichida handle'ni chaqirish
- 8.2 Satr ko'rinishidagi ref
- 8.3 Jamlanma
- 9. Mashqlar
- 1-mashq (oson): miqdor maydoni
- 2-mashq (o'rta): eskirgan izoh
- 3-mashq (qiyin): xatoli maydonga fokus
- 10. Real ishda
- Xulosa
- Manbalar
React useImperativeHandle: komponentning imperativ API'si — ota faqat kerakli metodlarni oladi
Qisqacha:
refprop orqali ota bola komponentning butun DOM elementini oladi va u bilan xohlaganini qila oladi.useImperativeHandle(ref, () => ({ focus, clear }), [])bilan bola o'rniga o'zi tanlagan kichik obyektni beradi — ota faqat shu metodlarni chaqira oladi. Bu fokus, aylantirish (scroll), matnni belgilash kabi faqat buyruq bilan bajariladigan ishlar uchun. Ochiq/yopiq kabi narsalarni esa props bilan boshqaring.
Bu darsda
useImperativeHandlenima uchun kerakligini varefprop'dan farqini tushuntira olasiz.- Bola komponentdan otaga
focus(),clear(),reset()kabi cheklangan API berasiz. - Handle'dagi eskirgan closure (stale closure) tuzog'ini bog'liqliklar bilan tuzatasiz.
- TypeScript'da handle turini
Ref<T>bilan yozasiz. - Qachon imperativ API, qachon props kerakligini ajrata olasiz.
Oldin bilishingiz kerak: useRef II: DOM elementlari va ref prop, useRef I: render chaqirmaydigan xotira, useEffect II: dependency massivi.
1. Nega bu kerak?
Oldingi darsda ref ni oddiy prop sifatida bola komponentga berdik. Bola uni <input ref={ref}> ga ulasa, ota haqiqiy DOM elementini oladi. Masalan, ota ref.current.focus() deb fokus qo'yadi.
«Bahor» menyu sahifasida SearchBox komponenti bor: qidiruv maydoni va tozalash tugmasi. Jasur aka so'radi: "Sahifadagi «Qidiruvga o'tish» tugmasini bossam, kursor qidiruvga tushsin." Sardor ref ni to'g'ridan-to'g'ri <input> ga uladi. Ishladi.
Bir hafta o'tib boshqa dasturchi ota komponentda shunday yozdi: search.current.value = "osh". Ekranda "osh" paydo bo'ldi, lekin SearchBox ning state'i bo'sh qoldi. Natija — qidiruv ishlamadi, chunki React bu o'zgarishdan bexabar. Muammo shundaki, ota butun <input> ni oldi: value, style, remove() — hammasini.
Hayotiy o'xshatish: oshxonaning tarqatish darchasi. Ofitsiant darchadan buyurtma beradi va tayyor taomni oladi. Unga oshxona kalitini bermaysiz — ichkariga kirib, qozonni o'zi aralashtirmasin. useImperativeHandle — shunday darcha. Bola komponent otaga faqat o'zi tanlagan "tugmalarni" ko'rsatadi.
2. Imperativ va deklarativ: eslatma
2.1 Brauzerdagi buyruqlar
React nima va nega darsida ikki uslubni ko'rgan edik. Deklarativ — "ekran qanday bo'lsin" deysiz: <p>{count}</p>. Imperativ — "nima qil" deysiz: p.textContent = count.
React'da deyarli hamma narsa deklarativ: matn, rang, ko'rinish — props va state'dan. Lekin brauzerda ba'zi narsalar faqat buyruq bilan bajariladi. Ularning atributi yo'q, faqat metodi bor:
| Ish | DOM metodi | Atribut bilan bo'ladimi? |
|---|---|---|
| Fokus qo'yish | input.focus() |
yo'q |
| Matnni belgilash | input.select() |
yo'q |
| Elementgacha aylantirish | el.scrollIntoView() |
yo'q |
| Video ijrosi | video.play() |
yo'q |
| Modal dialog | dialog.showModal() |
yo'q |
Vanilla JavaScript'da bularni document.querySelector(...) bilan topib chaqirardingiz. React'da element ref orqali olinadi (useRef II).
2.2 ref prop bilan butun element ketadi
Bola ref ni <input> ga ulasa, otaga haqiqiy HTMLInputElement keladi. Uning yuzlab xususiyati va metodi bor. Ota ulardan istaganini ishlata oladi — jumladan, React boshqarayotgan narsalarni ham. Bu bola komponentning "ichki ishi" ochilib qolgani demak. Keyinchalik bola o'zgarsa (masalan, <input> o'rniga <textarea> qo'yilsa), otadagi kod jim buziladi.
Tekshirib ko'ring: Ota
search.current.value = "osh"deb yozdi,SearchBoxesa controlled (Formalar I):value={query}. Nega qidiruv ishlamaydi?
Javob
Controlled maydonda "haqiqat" — state (query). Ota DOM'ga to'g'ridan-to'g'ri yozdi, onChange chaqirilmadi, query bo'sh qoldi. Qidiruv natijasi state'dan hisoblanadi — u o'zgarmadi. Keyingi renderda React esa maydonga yana query ni yozadi va "osh" yo'qolishi mumkin.
3. useImperativeHandle sintaksisi
3.1 Birinchi misol
import {
useEffect,
useImperativeHandle,
useRef,
useState,
} from "react";
function SearchBox({ ref }) {
const input = useRef(null); // ichki ref — faqat bolaniki
const [query, setQuery] = useState("");
useImperativeHandle(ref, () => ({
focus() {
input.current.focus();
},
clear() {
setQuery("");
},
}), []);
return (
<input
ref={input}
aria-label="Qidiruv"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
);
}
function App() {
const search = useRef(null);
console.log("render:", search.current);
useEffect(() => {
console.log("effekt:", Object.keys(search.current));
}, []);
function goToSearch() {
search.current.focus();
const active = document.activeElement;
console.log("fokusda:", active.getAttribute("aria-label"));
}
return (
<div>
<SearchBox ref={search} />
<button onClick={goToSearch}>Qidiruvga o'tish</button>
</div>
);
}Konsolda:
render: null
effekt: (2) ['focus', 'clear']
fokusda: QidiruvEkranda:
Qidiruvga o'tishUchta argument:
ref— otadan kelgan ref (React 19 da oddiy prop, useRef II).- Yaratuvchi funksiya — otaga beriladigan obyektni qaytaradi. Bu obyekt handle ("dastak", ushlagich) deb ataladi.
- Bog'liqliklar massivi —
useEffectdagidek: qachon handle qayta yaratilsin.
Endi search.current — <input> emas, ikki metodli oddiy obyekt. Konsoldagi (2) ['focus', 'clear'] shuni ko'rsatyapti. Ota value ni ham, style ni ham ko'rmaydi.
SearchBox ichida ikkita ref bor. input — bolaning o'z ref'i, haqiqiy <input> ga ulangan. ref — otaniki, unga React handle'ni qo'yadi. Bola ichkarida DOM bilan bemalol ishlaydi, tashqariga esa faqat darchani ochadi.
3.2 Handle qachon paydo bo'ladi
Konsolning birinchi qatori render: null. Render paytida handle hali yo'q — React uni DOM o'zgarishlarini qo'llagandan keyin (commit bosqichida) qo'yadi. Bu oddiy DOM ref'idagi bilan bir xil qoida (useRef II).
| Qadam | search.current |
Ekranda |
|---|---|---|
App birinchi render |
null |
hali hech narsa |
| commit: DOM tayyor, handle qo'yildi | {focus, clear} |
maydon va tugma |
| effektlar | {focus, clear} |
o'zgarishsiz |
| «Qidiruvga o'tish» bosildi | {focus, clear} |
kursor qidiruvda |
SearchBox olib tashlandi |
null |
maydon yo'q |
Oxirgi qatorni tekshiramiz. Komponent ekrandan olib tashlansa, React ref'ga yana null yozadi:
import { useImperativeHandle, useRef, useState } from "react";
function SearchBox({ ref }) {
useImperativeHandle(ref, () => {
console.log("handle yaratildi");
return { focus() {} };
}, []);
return <input aria-label="Qidiruv" />;
}
function App() {
const search = useRef(null);
const [shown, setShown] = useState(true);
function remove() {
setShown(false);
setTimeout(() => console.log("keyin:", search.current), 0);
}
return (
<div>
{shown && <SearchBox ref={search} />}
<button onClick={remove}>Olib tashlash</button>
</div>
);
}Konsolda:
handle yaratildi
keyin: nullEkranda:
Olib tashlashShuning uchun handle'ni hodisa ishlovchisida yoki effektda chaqiring — render ichida emas. Render paytida u null bo'ladi va null.focus() xato beradi (pastda, «Ko'p uchraydigan xatolar» da).
Tekshirib ko'ring:
useImperativeHandlening yaratuvchi funksiyasi{ focus() {...} }qaytarmay,input.currentni qaytarsa nima bo'ladi?
Javob
Ota yana butun <input> ni oladi — xuddi ref ni to'g'ridan-to'g'ri <input> ga ulagandek. Hook ishlaydi, lekin uning foydasi yo'qoladi: darcha o'rniga eshik ochildi. Handle'ga faqat otaga kerak bo'lgan metodlarni qo'ying.
4. Handle — faqat DOM emas
4.1 Bir nechta element va state
Handle metodlari ichida istalgan kod bo'lishi mumkin: bir nechta ref, state yangilash, hisob-kitob. «Bahor» kassasida buyurtma formasi bor. Kassir «Yangi buyurtma» ni bossa, forma tozalanishi va kursor ism maydoniga tushishi kerak:
import { useImperativeHandle, useRef, useState } from "react";
function OrderForm({ ref }) {
const nameInput = useRef(null);
const [name, setName] = useState("");
const [qty, setQty] = useState("1");
useImperativeHandle(ref, () => ({
reset() {
setName("");
setQty("1");
nameInput.current.focus();
},
}), []);
return (
<div>
<p>
<label>
Ism
<input ref={nameInput} value={name}
onChange={(e) => setName(e.target.value)} />
</label>
</p>
<p>
<label>
Soni
<input value={qty}
onChange={(e) => setQty(e.target.value)} />
</label>
</p>
<p>Buyurtma: {name || "—"}, {qty} ta osh</p>
</div>
);
}
function App() {
const form = useRef(null);
return (
<section>
<OrderForm ref={form} />
<button onClick={() => form.current.reset()}>
Yangi buyurtma
</button>
</section>
);
}Ekranda:
Ism
Soni
Buyurtma: —, 1 ta osh
Yangi buyurtmaHarakat qatori avval ikki maydonni to'ldirdi ("Ali", 3), keyin «Yangi buyurtma» ni bosdi. reset() ikkala state'ni ham tikladi va fokusni qo'ydi. Ota formaning ichida nechta maydon borligini ham, ular qanday state'da saqlanishini ham bilmaydi. U faqat bitta so'zni biladi: reset.
Bu — cheklangan API (application programming interface — "dasturlash interfeysi", ya'ni boshqa kod sizning kodingizdan foydalanadigan eshiklar ro'yxati). Bola komponentning ichi keyin o'zgarishi mumkin: maydonlar ko'payadi, state reducer'ga ko'chadi. Ota kodi esa o'zgarmaydi — reset() o'sha-o'sha.
4.2 Handle stateni o'qisa: eskirgan qiymat
Yangi talab: ota izoh matnini o'qiy olsin. Sardor getText() qo'shdi:
import { useImperativeHandle, useRef, useState } from "react";
function NoteBox({ ref }) {
const [text, setText] = useState("");
useImperativeHandle(ref, () => ({
getText: () => text,
}), []); // ❌ text bog'liqliklarda yo'q
return (
<input aria-label="Izoh" value={text}
onChange={(e) => setText(e.target.value)} />
);
}
function App() {
const note = useRef(null);
function read() {
console.log(JSON.stringify(note.current.getText()));
}
return (
<div>
<NoteBox ref={note} />
<button onClick={read}>O'qish</button>
</div>
);
}Konsolda:
""Ekranda:
O'qishMaydonda "Piyozsiz" turibdi, getText() esa bo'sh satr qaytardi. Sabab tanish — eskirgan closure (stale closure, useEffect II darsida ko'rgansiz). Handle birinchi renderda yaratildi va o'sha renderdagi text ni ("") eslab qoldi. Bog'liqliklar [] — React handle'ni boshqa hech qachon yangilamadi.
| Qadam | text (state) |
handle eslagan text |
|---|---|---|
| birinchi render | "" |
"" |
| "Piyozsiz" yozildi, qayta render | "Piyozsiz" |
"" (yangilanmadi) |
| «O'qish» | "Piyozsiz" |
"" → konsolda "" |
Tuzatish — text ni bog'liqliklarga qo'shish:
import { useImperativeHandle, useRef, useState } from "react";
function NoteBox({ ref }) {
const [text, setText] = useState("");
useImperativeHandle(ref, () => ({
getText: () => text,
}), [text]); // ✅ text o'zgarsa — handle yangisi
return (
<input aria-label="Izoh" value={text}
onChange={(e) => setText(e.target.value)} />
);
}
function App() {
const note = useRef(null);
function read() {
console.log(JSON.stringify(note.current.getText()));
}
return (
<div>
<NoteBox ref={note} />
<button onClick={read}>O'qish</button>
</div>
);
}Konsolda:
"Piyozsiz"Ekranda:
O'qishEndi har harfda React eski handle'ni olib, yangisini qo'yadi. Bu xatoni linter oldindan aytadi. eslint-plugin-react-hooks 7.1.1 ning exhaustive-deps qoidasi useImperativeHandle ni ham tekshiradi (Hooklar qoidalari). Birinchi variantni npx eslint bilan tekshirdik:
7:7 warning React Hook useImperativeHandle has a missing dependency: 'text'. Either include it or remove the dependency array react-hooks/exhaustive-deps"useImperativeHandle da text bog'liqligi yetishmayapti. Uni qo'shing yoki bog'liqliklar massivini olib tashlang." vazifalar da lint skripti --max-warnings=0 bilan ishlaydi — bunday ogohlantirish commit'ga o'tmaydi.
Bog'liqliklar massivini umuman yozmasa ham bo'ladi — unda handle har renderda qayta yaratiladi. Bu xato emas, faqat ortiqcha ish. focus() va reset() kabi faqat ref va setter ishlatadigan metodlar uchun [] to'g'ri: ref obyekti va setX funksiyalari renderlar orasida o'zgarmaydi.
Maslahat: Ko'pincha ota bola state'ini o'qishi shart emas. Agar shunday ehtiyoj tug'ilsa, avval o'ylang: balki bu state otada bo'lishi kerakdir (State'ni ko'tarish)? Bola
onChangebilan xabar beradi, ota esa o'zi saqlaydi.
5. TypeScript'da handle turi
5.1 Ref<T> — handle turi
Handle — oddiy obyekt, uning turini o'zingiz yozasiz. Bola ref prop'ini Ref<SearchHandle> deb e'lon qiladi, ota esa useRef<SearchHandle>(null):
import { useImperativeHandle, useRef } from "react";
import type { Ref } from "react";
export type SearchHandle = {
focus: () => void;
};
function SearchBox({ ref }: { ref?: Ref<SearchHandle> }) {
const input = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => ({
focus() {
input.current?.focus();
},
}), []);
return <input ref={input} aria-label="Qidiruv" />;
}
export default function App() {
const search = useRef<SearchHandle>(null);
return (
<div>
<SearchBox ref={search} />
<button onClick={() => search.current?.focus()}>
Qidiruvga o'tish
</button>
</div>
);
}Ekranda:
Qidiruvga o'tishref?: — ixtiyoriy: ota ref bermasa ham SearchBox ishlaydi. useImperativeHandle ref ning undefined bo'lishini o'zi ko'taradi. ?. (?? va ?.) — current null bo'lishi mumkinligini TypeScript eslatadi.
5.2 TypeScript ortiqcha so'rovni ushlaydi
Ota endi qidiruv maydonining rangini o'zgartirmoqchi:
import { useImperativeHandle, useRef } from "react";
import type { Ref } from "react";
type SearchHandle = { focus: () => void };
function SearchBox({ ref }: { ref?: Ref<SearchHandle> }) {
const input = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => ({
focus() { input.current?.focus(); },
}), []);
return <input ref={input} aria-label="Qidiruv" />;
}
export default function App() {
const search = useRef<SearchHandle>(null);
function paint() {
search.current!.style.color = "red";
}
return <SearchBox ref={search} />;
}Xato:
TS2339: Property 'style' does not exist on type 'SearchHandle'."SearchHandle turida style xususiyati yo'q." Darcha qisqa — ota faqat focus ni ko'radi. TypeScript'siz esa xato ishlash paytida chiqadi:
import { useImperativeHandle, useRef } from "react";
function SearchBox({ ref }) {
const input = useRef(null);
useImperativeHandle(ref, () => ({
focus() { input.current.focus(); },
}), []);
return <input ref={input} aria-label="Qidiruv" />;
}
function App() {
const search = useRef(null);
return (
<div>
<SearchBox ref={search} />
<button onClick={() => { search.current.style.color = "red"; }}>
Qizil
</button>
</div>
);
}Konsolda:
Uncaught TypeError: Cannot set properties of undefined (setting 'color')
TypeError: Cannot set properties of undefined (setting 'color')"undefined ning xususiyatlarini o'rnatib bo'lmaydi (color ni o'rnatishda)." search.current.style — undefined, chunki handle'da style yo'q. Xato hodisa ishlovchisida chiqdi, shuning uchun ekran buzilmadi — faqat tugma ishlamadi. Bu — biz xohlagan cheklov: ota bolaning ichiga kira olmaydi.
6. Eski kod: forwardRef
forwardRef ning o'zini useRef II darsida ko'rgansiz: React 19 gacha funksiya komponent ref ni props'da ololmasdi, u o'ram orqali ikkinchi argument bo'lib kelardi. Eski loyihalar va kutubxonalarda useImperativeHandle aynan shu o'ram bilan birga uchraydi. Farq faqat ref qayerdan kelishida — hookning o'zi bir xil:
import { forwardRef, useImperativeHandle, useRef } from "react";
// React 18 uslubi: ref — ikkinchi argument
const OldSearch = forwardRef(function OldSearch(props, ref) {
const input = useRef(null);
useImperativeHandle(ref, () => ({
focus() { input.current.focus(); },
}), []);
return <input ref={input} aria-label="Eski qidiruv" />;
});
function App() {
const search = useRef(null);
function go() {
search.current.focus();
console.log("handle:", Object.keys(search.current).join(", "));
}
return (
<div>
<OldSearch ref={search} />
<button onClick={go}>Fokus</button>
</div>
);
}Konsolda:
handle: focusOta baribir <input> ni emas, faqat focus ni oldi. Eski kodni yangilashda ikki qadam yetadi: forwardRef(...) o'ramini olib tashlang va ref ni props'dan oling (function OldSearch({ ref })). React 19 e'loni (react.dev blog, 2024-12-05) forwardRef kelajakda eskirgan deb belgilanishini aytadi.
7. Qachon props afzalroq
7.1 Imperativ API — oxirgi chora
react.dev'dagi useImperativeHandle sahifasi aniq qoida beradi: ref'lardan faqat props bilan ifodalab bo'lmaydigan imperativ ishlar uchun foydalaning — elementgacha aylantirish, fokus, animatsiyani boshlash, matnni belgilash.
Sardor modal oyna yasadi va otaga open(), close() metodlarini berdi:
// ❌ ochiq/yopiq — bolaning yashirin state'i
modal.current.open();Bu ishlaydi, lekin uch muammo bor:
- Ota modal ochiqmi-yo'qmi bilmaydi. Sarlavhada "Modal ochiq" deb yozish uchun yana
isOpen()metodi kerak bo'ladi. - Holat URL'dan yoki serverdan kelsa (masalan,
?bron=1— bron oynasi ochiq), uni chizib bo'lmaydi: avval render, keyin effektdaopen(). - Ikki joy bitta narsani boshqaradi — "yagona haqiqat manbai" (State'ni ko'tarish) buziladi.
Deklarativ variant — ochiqlik ota state'ida, bola uni prop sifatida oladi:
import { useState } from "react";
function BookingModal({ isOpen, onClose }) {
if (!isOpen) return null;
return (
<div role="dialog" aria-label="Bron">
<p>Stol bron qilish — 10:00 dan 22:00 gacha</p>
<button onClick={onClose}>Yopish</button>
</div>
);
}
function App() {
const [isOpen, setIsOpen] = useState(false);
return (
<main>
<p>Modal: {isOpen ? "ochiq" : "yopiq"}</p>
<button onClick={() => setIsOpen(true)}>
Stol bron qilish
</button>
<BookingModal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
/>
</main>
);
}Ekranda:
Modal: yopiq
Stol bron qilishHarakatlar oynani ochdi va yopdi — oxirida yana "yopiq". Ota har doim holatni biladi va uni sarlavhada ishlatadi. open() metodi kerak emas.
7.2 Qaror jadvali
| Vazifa | Yechim | Nega |
|---|---|---|
| ochiq/yopiq, tanlangan tab, joriy slayd | props (isOpen, index) |
bu — holat, ota ham bilishi kerak |
| fokus qo'yish, matnni belgilash | handle (focus, select) |
brauzerda atributi yo'q |
| ro'yxat oxirigacha aylantirish | handle (scrollToEnd) |
scrollIntoView — buyruq |
| video yoki audio ijrosi | handle (play, pause) yoki prop |
DOM'da buyruq; holat ham bo'lishi mumkin |
| formani tozalash | prop (key) yoki handle (reset) |
ikkalasi ham to'g'ri |
Oxirgi qator haqida: formani butunlay yangidan boshlash uchun key ni o'zgartirish ham yetadi — React komponentni o'chirib, yangisini yaratadi (State'ning saqlanishi va key). Handle esa qo'shimcha ish kerak bo'lganda qulay: masalan, tozalashdan keyin fokus qo'yish.
Tekshirib ko'ring: «Bahor» menyusi karuselida (slayder) joriy taom URL'da saqlanadi:
?taom=3. KaruselgagoTo(3)metodi berasizmi yokiindex={3}prop'i?
Javob
index={3} prop'i. Joriy slayd — holat: u URL'dan keladi va boshqa joylar ham uni ko'rsatadi ("3 / 12"). Prop bilan karusel birinchi renderdanoq to'g'ri slaydda chiziladi. goTo(3) esa avval noto'g'ri slaydni chizib, keyin effektda o'tkazardi.
flowchart TD
S["Ota boladan nimadir xohlaydi"] --> Q{"Bu holatmi?<br/>ochiq, tanlangan, qiymat"}
Q -- "ha" --> P["props + onX"]
Q -- "yo'q, buyruq" --> D{"Butun DOM kerakmi?"}
D -- "faqat bitta element, kutubxona" --> R["ref prop"]
D -- "yo'q, bir necha metod" --> H["useImperativeHandle"]Diagramma qaror tartibini ko'rsatadi: avval "bu holatmi?" deb so'rang. Ko'p hollarda javob "ha" va masala props bilan yechiladi.
8. Ko'p uchraydigan xatolar
8.1 Render ichida handle'ni chaqirish
import { useImperativeHandle, useRef } from "react";
function SearchBox({ ref }) {
useImperativeHandle(ref, () => ({ focus() {} }), []);
return <input aria-label="Qidiruv" />;
}
function App() {
const search = useRef(null);
search.current.focus(); // ❌ renderda — handle hali yo'q
return <SearchBox ref={search} />;
}Konsolda:
Uncaught TypeError: Cannot read properties of null (reading 'focus')"null ning xususiyatlarini o'qib bo'lmaydi (focus ni o'qishda)." Birinchi renderda search.current — null. Tuzatish: chaqiruvni hodisa ishlovchisiga yoki effektga ko'chiring.
8.2 Satr ko'rinishidagi ref
Juda eski kodda (class komponentlar davri) ref="input" deb yozilardi. React 19 bu shaklni butunlay olib tashladi:
function SearchBox({ ref }) {
return <input ref={ref} aria-label="Qidiruv" />;
}
function App() {
return <SearchBox ref="search" />;
}Konsolda:
Uncaught Error: Expected ref to be a function, an object returned by React.createRef(), or undefined/null."Ref funksiya, React.createRef() qaytargan obyekt yoki undefined/null bo'lishi kutilgan edi." Tuzatish: useRef(null) yarating va o'shani bering.
8.3 Jamlanma
| Belgi | Sabab | Davosi |
|---|---|---|
Cannot read properties of null (reading 'focus') |
handle renderda chaqirildi | hodisa yoki effektda chaqiring |
| handle eski qiymat qaytaradi | bog'liqliklarda state yo'q | [text] qo'shing |
Cannot set properties of undefined |
handle'da yo'q xususiyat | handle'ga metod qo'shing yoki props ishlating |
| ota bola state'ini o'zgartiradi | butun DOM element ochilgan | useImperativeHandle bilan cheklang |
Expected ref to be a function… |
ref="satr" |
useRef |
9. Mashqlar
Mashqlar kurs/mashqlar/17/35-imperativ-api/ da. Har yechimni «Ishga tushir» bilan sinang.
1-mashq (oson): miqdor maydoni
QtyInput — taom soni maydoni. Ota faqat ikki narsa qila olsin: fokus qo'yish va matnni belgilash (select() — maydondagi butun matnni ajratadi, yangi son yozilsa eskisi o'chadi). Bo'sh joylarni to'ldiring:
function QtyInput({ ref }) {
const input = useRef(null);
[___:useImperativeHandle](ref, () => ({
focus() { input.current.focus(); },
select() { input.current.[___:select](); },
}), [___:[]]);
return <input ref={input} aria-label="Soni" defaultValue="1" />;
}Yechim
import { useImperativeHandle, useRef } from "react";
function QtyInput({ ref }) {
const input = useRef(null);
useImperativeHandle(ref, () => ({
focus() { input.current.focus(); },
select() { input.current.select(); },
}), []);
return <input ref={input} aria-label="Soni" defaultValue="1" />;
}
function App() {
const qty = useRef(null);
function edit() {
qty.current.focus();
qty.current.select();
console.log(Object.keys(qty.current));
}
return (
<div>
<QtyInput ref={qty} />
<button onClick={edit}>Sonini o'zgartirish</button>
</div>
);
}Konsolda:
(2) ['focus', 'select']Ekranda:
Sonini o'zgartirishBog'liqliklar []: metodlar faqat ref ishlatadi, ref obyekti esa o'zgarmaydi.
2-mashq (o'rta): eskirgan izoh
Ofitsiant izoh yozadi, kassir «Chekka yozish» tugmasini bosadi. Chekda har doim bo'sh izoh chiqyapti. Xatoni toping va isEmpty() metodini ham qo'shing (bo'sh joylardan iborat matn ham bo'sh hisoblansin — trim(), matnni tozalash).
import { useImperativeHandle, useRef, useState } from "react";
function NoteBox({ ref }) {
const [text, setText] = useState("");
useImperativeHandle(ref, () => ({ getText: () => text }), []);
return (
<input aria-label="Izoh" value={text}
onChange={(e) => setText(e.target.value)} />
);
}
function App() {
const note = useRef(null);
const print = () => console.log("Chek:", note.current.getText());
return (
<div>
<NoteBox ref={note} />
<button onClick={print}>Chekka yozish</button>
</div>
);
}Konsolda:
Chek:Yechim
import { useImperativeHandle, useRef, useState } from "react";
function NoteBox({ ref }) {
const [text, setText] = useState("");
useImperativeHandle(ref, () => ({
getText: () => text,
isEmpty: () => text.trim() === "",
}), [text]);
return (
<input aria-label="Izoh" value={text}
onChange={(e) => setText(e.target.value)} />
);
}
function App() {
const note = useRef(null);
function print() {
if (note.current.isEmpty()) {
console.log("Chek: izohsiz");
return;
}
console.log("Chek:", note.current.getText());
}
return (
<div>
<NoteBox ref={note} />
<button onClick={print}>Chekka yozish</button>
</div>
);
}Konsolda:
Chek: QalampirsizXato — bog'liqliklar []: handle birinchi renderdagi bo'sh text ni eslab qolgan edi. [text] bilan har o'zgarishda yangi handle yaratiladi. isEmpty ham text ni o'qiydi — u ham shu bog'liqlikka tayanadi.
3-mashq (qiyin): xatoli maydonga fokus
TypeScript'da OrderForm yozing: ism va son maydonlari, handle — OrderFormHandle:
reset()— ikkala maydonni tozalaydi (son —"1") va fokusni ism maydoniga qo'yadi;focusFirstError()— birinchi xatoli maydonga fokus qo'yadi va uning yorlig'ini qaytaradi: ism bo'sh bo'lsa"Mehmon", son 1 dan kichik yoki son bo'lmasa"Soni", xato yo'q bo'lsanull.
Ota «Yuborish» tugmasida focusFirstError() ni chaqiradi va natijani konsolga yozadi. Ishora: ikkita ichki ref kerak, Number(qty) va Number.isInteger yordam beradi.
Yechim
import { useImperativeHandle, useRef, useState } from "react";
import type { Ref } from "react";
export type OrderFormHandle = {
reset: () => void;
focusFirstError: () => string | null;
};
function OrderForm({ ref }: { ref?: Ref<OrderFormHandle> }) {
const nameInput = useRef<HTMLInputElement>(null);
const qtyInput = useRef<HTMLInputElement>(null);
const [name, setName] = useState("");
const [qty, setQty] = useState("1");
useImperativeHandle(ref, () => ({
reset() {
setName("");
setQty("1");
nameInput.current?.focus();
},
focusFirstError() {
if (name.trim() === "") {
nameInput.current?.focus();
return "Mehmon";
}
const n = Number(qty);
if (!Number.isInteger(n) || n < 1) {
qtyInput.current?.focus();
return "Soni";
}
return null;
},
}), [name, qty]);
return (
<div>
<p>
<label>
Mehmon
<input ref={nameInput} value={name}
onChange={(e) => setName(e.target.value)} />
</label>
</p>
<p>
<label>
Soni
<input ref={qtyInput} value={qty}
onChange={(e) => setQty(e.target.value)} />
</label>
</p>
</div>
);
}
export default function App() {
const form = useRef<OrderFormHandle>(null);
function submit() {
const error = form.current?.focusFirstError() ?? null;
console.log("Xato maydon:", error);
const active = document.activeElement;
console.log("Fokusda:", active?.closest("label")?.textContent);
}
return (
<section>
<OrderForm ref={form} />
<button onClick={submit}>Yuborish</button>{" "}
<button onClick={() => form.current?.reset()}>Tozalash</button>
</section>
);
}Konsolda:
Xato maydon: Soni
Fokusda: SoniEkranda:
Mehmon
Soni
Yuborish TozalashfocusFirstError state'ni o'qiydi — bog'liqliklar [name, qty]. Ota formaning tuzilishini bilmaydi: maydon qo'shilsa, faqat OrderForm o'zgaradi. Tozalash va xatoga fokus — ikkalasi ham "buyruq", holat emas: shuning uchun bu yerda handle o'rinli.
10. Real ishda
- UI kutubxonalari (masalan, kod muharriri, xarita, video pleyer, matn muharriri) komponentlari ko'pincha ref orqali cheklangan API beradi:
editor.focus(),map.flyTo(...),player.seekTo(...). Ularning hujjatidagi "ref methods" bo'limi — aynanuseImperativeHandle. - Dizayn tizimlari (kompaniyaning umumiy komponentlari)
Input,Selectkabi komponentlardafocus()ni ochadi — formalar xatoli maydonga fokus qo'yishi uchun (Fokus boshqaruvi). - Kod ko'rigida "
open()metodi o'rnigaisOpenprop qiling" — tez-tez uchraydigan izoh. - Intervyu savollari: "
useImperativeHandlenima uchun kerak?", "refprop bilan farqi nima?", "React 19 daforwardRefnima bo'ldi?", "Qachon imperativ API — yomon tanlov?".
Xulosa
refprop otaga butun DOM elementini beradi;useImperativeHandleesa faqat tanlangan metodlarni.- Handle commit'da qo'yiladi: renderda
null, komponent olib tashlansa yananull. - Handle state o'qisa — state bog'liqliklarda bo'lsin, aks holda eskirgan qiymat qaytadi.
- TypeScript'da
ref?: Ref<Handle>— ota handle'da yo'q narsani so'rasa,TS2339. - React 19 da
forwardRefkerak emas; eski kodda uni o'qiy olasiz. - Holat (ochiq, tanlangan) — props; buyruq (fokus, aylantirish, belgilash) — handle.
Keyingi dars: useLayoutEffect va vizual miltillashning oldini olish — DOM'ni o'lchab, ekran chizilishidan oldin joylashuvni tuzatishni o'rganamiz.
Manbalar
- React:
useImperativeHandle— react.dev/reference/react/useImperativeHandle - React: "Manipulating the DOM with Refs" — react.dev/learn/manipulating-the-dom-with-refs
- React 19 e'loni: "ref as a prop" (2024-12-05) — react.dev/blog/2024/12/05/react-19
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!