فوروارد کردن ref ها

ref فوروارد کردن یک تکنیک برای ارسال خودکار یک ref از طریق یک کامپوننت به فرزندانش می‌باشد. این کار معمولا برای بسیاری از کامپوننت‌ها در اپلیکیشن لازم نیست. به هر حال، در بعضی از کامپوننت‌ها، به خصوص در کامپوننت‌های کتابخانه‌ای با قابلیت استفاده مجدد می‌تواند مفید باشد. رایج‌ترین حالات ممکن در ادامه شرح داده شده‌اند.

فوروارد کردن ref ها به کامپوننت‌های DOM

یک کامپوننت FancyButton که المنت محلی button در DOM را رندر می‌کند در نظر بگیرید:

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

کامپوننت‌های ری‌اکت جزئیات پیاده سازی، شامل خروجی رندر شده خود را پنهان می‌کنند. کامپوننت‌های دیگر که از FancyButton استفاده می‌کنند، معمولا به بدست آوردن یک ref به المنت DOM button داخلی نیازی نخواهند داشت و این به علت جلوگیری از تکیه بیش از اندازه کامپوننت‌ها به ساختار DOM یک‌دیگر خوب است.

اگرچه چنین کپسوله‌سازی برای کامپوننت‌هایی در سطح اپ مانند FeedStory یا Comment مطلوب است، برای کامپوننت‌های کوچک که قابلیت استفاده مجدد بسیار زیادی دارند مانند FancyButton یا MyTextInput نامناسب می‌باشد. این کامپوننت‌ها میل به استفاده شدن در اپ به یک شیوه مشابه به عنوان یک button و input معمولی DOM دارند و دسترسی به نودهای DOM آن ها برای مدیریت انیمیشن‌ها، focus و selection ممکن است اجتناب ناپذیر باشد.

فوروارد کردن ref یک قابلیت انتخابی است که به بعضی کامپوننت ها اجازه گرفتن یک ref و انتقال آن به فرزندان (به عبارت دیگر, “فوروارد کردن” آن) را می‌دهد.

در مثال زیر، FancyButton از React.forwardRef برای گرفتن ref که به آن منتقل شده استفاده می‌کند، و سپس آن را به button DOM که رندر می‌کند فوروارد می‌کند.

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

// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;

با این روش، کامپوننت‌هایی که از FancyButton استفاده می‌کنند، می‌توانند یک ref به نود button در DOM بگیرند و در صورت نیاز، به همان شکلی که از یک button در DOM مستقیما استفاده می کنند، از این کامپوننت نیز بهره ببرند.

در زیر، یک توضیح مرحله به مرحله از آن‌چه در مثال بالا اتفاق افتاده است را مشاهده می‌نمایید:

  1. ما با فراخوانی React.createRef و اختصاص آن به متغیر ref یک ref ری‌اکت ایجاد می‌کنیم.
  2. با مشخص کردن ref به عنوان یک خصوصیت JSX، آن را به <FancyButton ref={ref}> منتقل می‌کنیم.
  3. ری‌اکت آن ref را به عنوان آرگومان دوم تابع (props, ref) => ... درون forwardRef پاس می‌دهد.
  4. ما این آرگومان ref را با تعیین کردن آن به عنوان یک خصوصیت JSX به <button ref={ref}> فوروارد می‌کنیم.
  5. وقتی که ref متصل شده‌است، ref.current به نود <button> در DOM اشاره می‌کند.

نکته

آرگومان دوم ref فقط زمانی که یک کامپوننت را با React.forwardRef فراخوانی کنید موجود است. کامپوننت‌های تابعی و بر پایه کلاس معمولی، آرگومان ref را دریافت نمی‌کنند و ref در props نیز در دسترس نمی‌باشد.

فوروارد کردن ref محدود به کامپوننت‌های DOM نیست. شما می‌توانید ref ها را به instance های کامپوننت‌های بر پایه کلاس نیز فوروارد کنید.

نکته برای نگهدارندگان کتابخانه کامپوننت‌ها

زمانی که شما شروع به استفاده از forwardRef در کامپوننت یک کتابخانه می‌کنید، باید به عنوان یک تغییر مخرب با آن رفتار کنید و یک ورژن major جدید از کتابخانه خود منتشر نمایید. به این علت که کتابخانه شما، یک رفتار متفاوت قابل توجه (مانند چیزی که ref به آن تخصیص داده شده و چه type هایی export شده‌اند) خواهد داشت و باعث تخریب برنامه‌ها و کتابخانه‌های دیگر که به رفتار قدیمی وابسته هستند می‌شود.

استفاده از React.forwardRef مشروط به این‌که وجود داشته‌باشد نیز به دلیل یکسان پیشنهاد نمی‌شود: چرا که چگونگی رفتار کتابخانه شما را تغییر می دهد و برنامه کاربرانتان را زمانی که ری‌اکت خود را ارتقا می‌دهد خراب می‌کند.

فوروارد کردن ref ها در کامپوننت‌های مرتبه بالاتر

این تکنیک در کامپوننت های مرتبه بالاتر (شناخته شده به نام HOC ها) نیز، می تواند مفید باشد. بیایید با یک مثال از HOC که prop های کامپوننت را در کنسول، نمایش می‌دهد شروع کنیم:

function logProps(WrappedComponent) {  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

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

  return LogProps;
}

کامپوننت مرتبه بالاتر “logProps” کل props را به کامپوننتی که پوشش داده‌است منتقل می‌کند، بنابراین خروجی رندر شده یکسان خواهد بود. برای مثال، می‌توانیم از این HOC برای مشاهده تمامی prop هایی که به کامپوننت “fancy button” ما منتقل شده‌است استفاده کنیم:

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

  // ...
}

// Rather than exporting FancyButton, we export LogProps.
// It will render a FancyButton though.
export default logProps(FancyButton);

فقط یک اخطار در مورد مثال بالا وجود دارد: ref ها منتقل نمی‌شوند. به علت اینکه ref یک prop نیست. مانند key، توسط ری‌اکت به شکل متفاوتی کنترل می‌شود. اگر شما یک ref به HOC اضافه کنید، ref به جای کامپوننت دربر گیرنده، به بیرونی ترین کامپوننت پوشاننده (container) اشاره می کند. این به این معناست که ref تعیین شده برای FancyButton، در اصل به کامپوننت LogProps متصل خواهد شد:

import FancyButton from './FancyButton';

const ref = React.createRef();
// The FancyButton component we imported is the LogProps HOC.
// Even though the rendered output will be the same,
// Our ref will point to LogProps instead of the inner FancyButton component!
// This means we can't call e.g. ref.current.focus()
<FancyButton
  label="Click Me"
  handleClick={handleClick}
  ref={ref}/>;

خوشبختانه، با استفاده از API های React.forwardRef، می توانیم به طور صریح ref ها را به کامپوننت داخلی FancyButton فوروارد کنیم. React.forwardRef یک تابع رندر که پارامترهای props و ref را دریافت می‌کند و یک نود ری‌اکت را به عنوان خروجی می‌دهد را می‌پذیرد. برای مثال:

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      const {forwardedRef, ...rest} = this.props;
      // Assign the custom prop "forwardedRef" as a ref
      return <Component ref={forwardedRef} {...rest} />;    }
  }

  // Note the second param "ref" provided by React.forwardRef.
  // We can pass it along to LogProps as a regular prop, e.g. "forwardedRef"
  // And it can then be attached to the Component.
  return React.forwardRef((props, ref) => {    return <LogProps {...props} forwardedRef={ref} />;  });}

نمایش یک نام سفارشی در DevTools

React.forwardRef یک تابع رندر می‌پذیرد. React DevTools از این تابع برای بررسی اینکه چه چیزی را برای کامپوننت ref forwarding نمایش دهد استفاده می‌کند.

برای مثال، کامپوننت زیر به عنوان ”ForwardRef” در DevTools ظاهر می‌شود:

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

اگر شما تابع رندر را نامگذاری کنید، DevTools نام آن‌را نیز به‌کار می‌برد. (برای مثال ”ForwardRef(myFunction)”):

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

شما حتی می‌توانید ویژگی displayName تابع را تنظیم کنید تا کامپوننتی که پوشش می‌دهید را شامل شود:

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

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

  // Give this component a more helpful display name in DevTools.
  // e.g. "ForwardRef(logProps(MyComponent))"
  const name = Component.displayName || Component.name;  forwardRef.displayName = `logProps(${name})`;
  return React.forwardRef(forwardRef);
}

Is this page useful?Edit this page