Ref-lərin Yönləndirilməsi

Ref-in yönləndirilməsi - avtomatik ref komponentinin içindən onun bir uşağına ötürülməsi texnikasıdır. Bu texnika çox vaxt applikasiyadakı komponentlər üçün vacib deyil. Lakin, bəzi növ komponentlər üçün, xüsusilə yenidən istifadə olunan komponent kitabxanalarında, faydalı ola bilər. Çox vaxt rast gəlinən ssenarilər aşağıda qeyd olunub.

DOM komponentlərinə ref-lərin yönləndirilməsi

Nativ button DOM elementini render edən FancyButton komponentinə nəzər salın:

function FancyButton(props) {
  return (
    <button className="FancyButton">
      {props.children}
    </button>
  );
}

React komponentləri tətbiq detallarını, eləcə də onların render edilmiş nəticələrini gizlədir. Adətən FancyButton istifadə edən digər komponenlər FancyButton-ın daxili button DOM elementində olan ref-i əldə etmək üçün lazım deyil. Bu yaxşıdır, çünki bu elementlərin bir-birinin DOM strukturlarından çox arxayın olmağının qarşısını alır.

Buna baxmayaraq, belə inkapsulyasiyalar FeedStory və ya Comment kimi applikasiya-səviyyəli komponentlər üçün arzu olunandır. Bu FancyButton və ya MyTextInput kimi yüksək dərəcəli yenidən istifadə edilə bilən “leaf” komponentləri üçün əlverişsiz ola bilər. Bu komponentlər applikasiya boyunca bənzər bir şəkildə müntəzən DOM buttoninput kimi istifadə edilməyə meyillidir və onların DOM nodlarına fokus, seçmə və ya animasiyalar üçün girişi qaçılmazdır.

Ref-in yönləndirilməsi, komponentlərin qəbul etdikləri ref-i daha aşağı uşağa göndərməsi (başqa sözlə “yönləndirməsi”) xüsusiyyətidir.

Aşağıdakı nümunədə FancyButton, ona ötürülmüş ref-i əldə etmək üçün React.forwardRef istifadə edir və sonra onu render edən DOM button-a yönləndirir:

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

// İndi siz ref-i birdəfəlik DOM button-a ötürə bilərsiniz:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;

Bu yolla FancyButton istifadə edən komponentlər, FancyButton-da yerləşən button DOM nodunun ref-ini əldə edə bilər və bu ref ilə DOM button-dan birbaşa istifadə etdiyimiz kimi, FancyButton-dakı button DOM nodundan istifadə edə bilərik.

Yuxarıdakı misalın addım-addım izahatına aşağıda baxa bilərsiniz:

  1. Biz React.createRef çağıraraq React ref yaradırıq və onu ref dəyişəninə təyin edirik.
  2. Biz ref-imizi JSX atributu kimi təyin edərək <FancyButton ref={ref}> komponentinə ötürürük.
  3. React ref-i forwardRef funskiyasının içində olan (props, ref) => ... funksiyasının ikinci arqumentinə ötürür.
  4. Biz bu ref arqumentini JSX atributu kimi təyin edərək <button ref={ref}> komponentinə yönləndiririk.
  5. ref qoşulduğu zaman, ref.current-i <button> DOM nodu göstərəcək.

Qeyd

Siz yalnız komponenti React.forwardRef ilə çağırışı ilə təyin etdikdə, ikinci ref arqumenti mövcud olacaqdır. Müntəzəm funksiya və ya klas komponentləri ref arqumentini qəbul etmir və ref proplarda da əlçatan deyil.

Ref-in yönləndirilməsi DOM komponentləri ilə limitli deyil. Siz həmçinin ref-ləri klas komponentlərinin nümunələrinə yönləndirə bilərsiniz.

Komponent Kitabxana saxlayıcıları üçün qeyd

Komponent kitabxanasında forwardRef istifadə etdikdə, siz onu sına bilən dəyişən kimi saxlamalı və öz kitabxananızın yeni böyük versiyasını yayımlamalısınız. Bu çox gümanki sizin kitabxananızın gözlənilən fərqli davranışı olduğu üçündür (məsələn, hansı ref-lərin təyin edildiyi, hansı növlərin ixrac edildiyi) və bu keçmiş davranışdan asılı olan applikasiyaları və digər kitabxanaları sındıra bilər.

Şərti şəkildə mövcud olduğu zaman React.forwardRef-i tətbiq etmək bəzi səbəblərə görə məsləhət görülmür: bu sizin kitabxananızın davranışını dəyişir və React-in özünü yenilədikdə etdikdə sizin istifadəçilərinizin applikasiyaları sınmasına səbəb ola bilər.

Yüksək qaydada komponentlərdə ref-lərin yönləndirilməsi

Bu metod yüksək qaydada komponentlər (higher-order components və ya HOCs) ilə xüsusilə faydalı ola bilər. Gəlin konsola komponent proplarının qeydiyyatını edən HOC-un misalı ilə başlayaq:

function logProps(WrappedComponent) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('köhnə proplar:', prevProps);
      console.log('yeni proplar:', this.props);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  }

  return LogProps;
}

“logProps” HOC-i, bütün propları onu əhatə edən komponentdən keçirir, beləliklə render edilmiş nəticə eyni olur. Misal üçün, biz bu HOC-ni bizim “fancy button” komponentimizə ötən bütün propları qeydiyyata almaq üçün istifadə edə bilərik:

class FancyButton extends React.Component {
  focus() {
    // ...
  }

  // ...
}

// FancyButton-u eksport etməkdənsə, biz LogProps-u eksport edirik.
// Buna baxmayaraq, bu FancyButton-u render edəcək.
export default logProps(FancyButton);

Yuxarıdakı nümunədə bir xəbərdarlıq var: ref-lər ötməyəcəklər. Bunun səbəbi ref-in prop olmamağıdır. key kimi bu React-də fərqli işlənir. Əgər siz HOC-aəref əlavə etsəniz, ref əhatə olunmuş komponentə deyil, ən xarici konteyner komponentinə müraciət edəcək.

Bu deməkdir ki, FancyButton-da komponentimiz üçün nəzərdə tutulmuş ref-lər faktiki olaraq LogProps komponentinə qoşulacaq:

import FancyButton from './FancyButton';

const ref = React.createRef();

// Bizim import etmidiyimiz FancyButton komponenti LogProps HOC-dur.
// Hətta renden edilmiş nəticə də eyni olacaq,
// Bizim ref-imiz daxili FancyButton komponentinin yerinə LogProps-a işarə edəcək!
// Bu deməkdir ki, biz məsələn ref.current.focus() çağıra bilmərik
<FancyButton
  label="Click Me"
  handleClick={handleClick}
  ref={ref}
/>;

Xoşbəxtlikdən, biz React.forwardRef API istifadə edərək açıq şəkildə ref-ləri FancyButton daxili komponentinə yönləndirə bilərik. React.forwardRef, propsref parametrlərini qəbul edən və React nodu qaytaran render funksiyasını qəbul edir. Məsələn:

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('köhnə proplar:', prevProps);
      console.log('yeni proplar:', this.props);
    }

    render() {
      const {forwardedRef, ...rest} = this.props;

      // Xüsusi prop "forwardedRef"-ı ref kimi təyin edin
      return <Component ref={forwardedRef} {...rest} />;
    }
  }

  // Nəzər alın ki, ikinci "ref" parametri React.forwardRef. tərəfindən təqdim olunub.
  // Biz bunu LogProps-a müntəzəm prop kimi ötürə bilərik, məs. "forwardedRef"
  // Və sonra bu Komponent kimi qoşula bilər.
  return React.forwardRef((props, ref) => {
    return <LogProps {...props} forwardedRef={ref} />;
  });
}

DevTools-da xüsusi adların göstərilməsi

React.forwardRef render funksiyasını qəbul edir. React DevTools, bu funksiyanı ref-in yönləndirilməsi komponentinin nə göstərəcəyini müəyyən etməsi üçün istifadə edir.

Misal üçün, növbəti komponent DevTools-da ”ForwardRef” kimi görünəcək:

const WrappedComponent = React.forwardRef((props, ref) => {
  return <LogProps {...props} forwardedRef={ref} />;
});

Əgər siz render funksiyasını adlandırsanız, DevTools da onun adını əlavə edəcək (məsələn: ”ForwardRef(myFunction)”):

const WrappedComponent = React.forwardRef(
  function myFunction(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }
);

Siz hətta funksiyanın displayName parametrini əhatə etdiyiniz komponentin adını daxil etmək üçün də quraşdıra bilərsiniz:

function logProps(Component) {
  class LogProps extends React.Component {
    // ...
  }

  function forwardRef(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }

  // DevTools-da bu komponentə daha faydalı təsvir adı ver.
  // məsələn "ForwardRef(logProps(MyComponent))"
  const name = Component.displayName || Component.name;
  forwardRef.displayName = `logProps(${name})`;

  return React.forwardRef(forwardRef);
}