Forward Ref — ref ni child componentga uzatish

~15 daq · React · Component Patterns

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