Forward Ref — ref ni child componentga uzatish
Muammodan boshlaylik
Loyihada dizayn tizimi bor. Input komponenti yozilgan — label, xato xabari, stil hammasi ichida:
function Input({ label, xato, ...props }) {
return (
<div className="input-wrapper">
{label && <label>{label}</label>}
<input className={xato ? 'input xato' : 'input'} {...props} />
{xato && <p className="xato-matn">{xato}</p>}
</div>
);
}
Forma sahifasida bu Input ni ishlatayapsiz. Forma yuborilganda birinchi bo'sh maydon avtomatik focusga olinishi kerak. Buning uchun ref kerak.
function LoginForma() {
const emailRef = useRef(null);
function yuborish(e) {
e.preventDefault();
if (!emailRef.current.value) {
emailRef.current.focus(); // bo'sh bo'lsa focusga ol
}
}
return (
<form onSubmit={yuborish}>
<Input ref={emailRef} label="Email" type="email" /> {/* ❌ ishlamaydi */}
</form>
);
}
Lekin bu ishlamaydi. ref — React ning maxsus mexanizmi, oddiy props kabi komponentga o'tmaydi. emailRef.current — Input komponentining DOM elementi emas, null bo'lib qoladi.
Nima uchun ref o'tmaydi?
React ref ni maxsus ko'radi — xuddi key kabi. Ular props ob'ektiga kirmaydi. Shuning uchun Input ichida props.ref deb olish mumkin emas.
Bu ataylab qilingan. Agar ref avtomatik o'tsa — har qanday komponent ichki DOM elementiga tashqaridan boshqarib bo'lardi. Bu encapsulation ni buzadi.
Lekin ba'zan kerak bo'ladi — dizayn tizimi komponentlari, input kutubxonalari. Buning uchun forwardRef bor.
forwardRef — yechim
forwardRef — "men ref ni qabul qilaman va ichkariga uzataman" degan signal.
import { forwardRef } from 'react';
const Input = forwardRef(function Input({ label, xato, ...props }, ref) {
// ↑
// ref — ikkinchi argument sifatida keladi
return (
<div className="input-wrapper">
{label && <label>{label}</label>}
<input
ref={ref} {/* ichki elementga uzatamiz */}
className={xato ? 'input xato' : 'input'}
{...props}
/>
{xato && <p className="xato-matn">{xato}</p>}
</div>
);
});
Endi ref ishlaydi:
function LoginForma() {
const emailRef = useRef(null);
const parolRef = useRef(null);
function yuborish(e) {
e.preventDefault();
if (!emailRef.current.value) {
emailRef.current.focus();
return;
}
if (!parolRef.current.value) {
parolRef.current.focus();
return;
}
// yuborish...
}
return (
<form onSubmit={yuborish}>
<Input ref={emailRef} label="Email" type="email" />
<Input ref={parolRef} label="Parol" type="password" />
<button type="submit">Kirish</button>
</form>
);
}
emailRef.current endi haqiqiy <input> DOM elementi — focus, value, blur hammasi ishlaydi.
useImperativeHandle — faqat kerakli narsani ochish
Ba'zan ref orqali hamma narsani emas, faqat ma'lum metodlarni ochish kerak. Masalan, video player — tashqaridan faqat play() va pause() ko'rinsin, boshqa xususiyatlar emas.
const VideoPlayer = forwardRef(function VideoPlayer({ src }, ref) {
const videoRef = useRef(null);
// ref.current ga faqat shu metodlar ko'rinadi
useImperativeHandle(ref, () => ({
play() {
videoRef.current.play();
},
pause() {
videoRef.current.pause();
},
}));
return <video ref={videoRef} src={src} />;
});
// Ishlatish
function App() {
const playerRef = useRef(null);
return (
<div>
<VideoPlayer ref={playerRef} src="/video.mp4" />
<button onClick={() => playerRef.current.play()}>▶ Play</button>
<button onClick={() => playerRef.current.pause()}>⏸ Pause</button>
{/* playerRef.current.src — ko'rinmaydi, faqat play/pause */}
</div>
);
}
Bu encapsulation — tashqaridan faqat ruxsat berilgan narsalar ko'rinadi.
Xulosa
ref oddiy props sifatida komponentga o'tmaydi — React uni maxsus ko'radi. forwardRef — "men ref ni qabul qilaman" degan signal, uni ichki elementga uzatish imkonini beradi.
forwardRef(function Komponent(props, ref) {
return <element ref={ref} />;
})
Dizayn tizimi komponentlari (Input, Button, Select) uchun muhim — ularni ishlatuvchi focus, scroll, o'lcham olish qilishi kerak bo'ladi.
Keyingi bo'lim
5-bo'lim: Stilizatsiya
35-dars: CSS Modules — nima, qanday ishlaydi, global va local