Ref به‌عنوان Prop در React مدرن

در درس‌های قبلی این فصل، Ref را برای دسترسی به عناصر پایه HTML مانند input یا div استفاده کردیم. اما یک سؤال طبیعی مطرح می‌شود: اگر بخواهیم یک Ref را به یک کامپوننت خودمان (نه یک عنصر پایه HTML) بدهیم تا از داخل آن به یک عنصر DOM درونی‌اش دسترسی پیدا کنیم، چه اتفاقی می‌افتد؟ پاسخ این سؤال، در طول زمان تغییر کرده و در این درس هر دو رویکرد را بررسی می‌کنیم.

مشکل: ref مثل یک Prop معمولی رفتار نمی‌کرد

فرض کنید یک کامپوننت به نام TextField داریم که خودش یک input را رندر می‌کند:

function TextField(props) {
  return <input {...props} />;
}

اگر بخواهیم از بیرون به این کامپوننت یک ref بدهیم تا به همان input داخلی دسترسی پیدا کنیم:

const inputRef = useRef(null);

<TextField ref={inputRef} />

در نسخه‌های قدیمی‌تر React، این کد به‌تنهایی کار نمی‌کرد؛ چون ref برخلاف سایر Propها، به‌طور خودکار به props.ref کامپوننت TextField منتقل نمی‌شد. React، ref را به‌عنوان یک ویژگی خاص و جداگانه از بقیه Propها در نظر می‌گرفت.

راه‌حل قدیمی: forwardRef (رویکرد Legacy)

برای حل این مشکل، React ابزاری به نام forwardRef ارائه داده بود که کامپوننت را در یک تابع خاص می‌پیچید تا بتواند ref را به‌عنوان آرگومان دوم، جدا از Propهای معمولی، دریافت کند:

// رویکرد قدیمی‌تر (Legacy)
const TextField = forwardRef((props, ref) => {
  return <input {...props} ref={ref} />;
});

این رویکرد سال‌ها راه‌حل استاندارد بود، اما نحو نسبتاً اضافه‌ای به کد تحمیل می‌کرد؛ هر کامپوننتی که می‌خواست ref بپذیرد، باید حتماً با forwardRef پیچیده می‌شد.

رویکرد مدرن: ref به‌عنوان یک Prop معمولی

در نسخه‌های جدیدتر React، این محدودیت برداشته شده است: یک Function Component می‌تواند ref را مستقیماً مانند هر Prop دیگری در پارامترهای خودش دریافت کند، بدون نیاز به forwardRef:

// رویکرد مدرن
function TextField({ ref, ...props }) {
  return <input {...props} ref={ref} />;
}

در این نسخه، TextField دقیقاً مانند دریافت هر Prop دیگری، ref را از طریق Destructuring دریافت می‌کند و مستقیماً آن را به عنصر input داخلی پاس می‌دهد؛ بدون هیچ پیچیدگی اضافه‌ای.

چرا این تغییر اهمیت دارد

این تغییر، نوشتن کامپوننت‌های قابل‌استفاده مجدد که نیاز به دسترسی بیرونی به یک عنصر DOM دارند را بسیار ساده‌تر می‌کند و یکی از مثال‌های ساده‌سازی‌هایی است که React در طول زمان روی نحو Function Components انجام داده است. با این حال، چون بسیاری از کدهای موجود همچنان از forwardRef استفاده می‌کنند، آشنایی با هر دو رویکرد برای خواندن و نگهداری کدهای قدیمی‌تر ضروری است.

مثال قابل اجرا

دریافت ref به‌عنوان Prop معمولی در یک کامپوننت
برای دیدن پیش‌نمایش کامپوننت، روی Run بزنید.

جمع‌بندی

در گذشته، برای این‌که یک کامپوننت بتواند ref دریافت کند و آن را به یک عنصر داخلی خودش منتقل کند، باید حتماً با forwardRef پیچیده می‌شد. در React مدرن، این نیاز برطرف شده و ref را می‌توان مانند هر Prop دیگری، مستقیماً در پارامترهای Function Component دریافت کرد. در درس بعدی به useImperativeHandle می‌پردازیم که کنترل دقیق‌تری روی آنچه از طریق یک Ref در دسترس قرار می‌گیرد فراهم می‌کند.