useRef — یک hook React است که یک شی ref قابل تغییر با ویژگی .current ایجاد میکند که بین رندرهای کامپوننت حفظ میشود. برخلاف useState، تغییر .current باعث رندر مجدد نمیشود، که useRef را برای ذخیره ارجاعهای DOM، تایمرها و هر مقداری که باید بدون بازکشیدن UI از رندرها عبور کند، ایدهآل میسازد. طبق React Documentation (2025)، شی ref یک بار در طول عمر کامپوننت ایجاد میشود و بین رندرها تغییر نمیکند که پایداری ارجاع را تضمین میکند.
نکات اصلی
useRef — یک hook است که در React 16.8 اضافه شده و یک شی ref قابل تغییر با یک ویژگی .current برمیگرداند. مقدار اولیه هنگام mount شدن کامپوننت به .current اختصاص داده میشود. تفاوت اصلی با یک متغیر معمولی — شی ref از رندرهای مجدد عبور میکند: در رندر مجدد، React ref را دوباره ایجاد نمیکند، بلکه همان شی را برمیگرداند.
پیادهسازی داخلی useRef از همان مکانیزم useState و useEffect استفاده میکند — لیست پیوندی hookها روی گره fiber. با این حال، برخلاف useState، React تغییرات شی ref را ردیابی نمیکند، بهروزرسانیها را در صف قرار نمیدهد و رندر مجدد را برنامهریزی نمیکند. این باعث میشود useRef یک hook بسیار سبک باشد که حتی با تغییرات مکرر بر عملکرد تأثیر نمیگذارد.
طبق React Team — Hooks FAQ (2025)، useRef اساساً یک «جعبه» است که مقدار را ذخیره میکند. شکر نحوی: const ref = useRef(initialValue) معادل const ref = { current: initialValue } است با این تفاوت که React تضمین میکند شی ref در هر رندر یکسان خواهد بود. هیچ جادویی — فقط یک ارجاع پایدار که توسط React مدیریت میشود.
import { useRef } from 'react';
function Component() {
const countRef = useRef(0);
// countRef.current = 0 در ابتدا
// countRef.current = 5 پس از تغییر
// رندر مجدد رخ نمیدهد!
}
رایجترین سناریوی استفاده از useRef — دسترسی مستقیم به عنصر DOM است. React بعد از mount شدن کامپوننت، elementRef.current را به ارجاع گره DOM اختصاص میدهد. این برای فوکوس کردن input، اندازهگیری ابعاد عنصر، ادغام با کتابخانههای انیمیشن و کتابخانههایی که از رندر React استفاده نمیکنند ضروری است.
React به طور خودکار ارجاعهای ref به DOM را مدیریت میکند: هنگام mount گره DOM واقعی را اختصاص میدهد، هنگام unmount آن را صفر میکند (null قرار میدهد). این تضمین میکند که .current همیشه مقدار بهروز دارد و از نشت حافظه از طریق ارجاعهای «آویزان» به عناصر حذف شده جلوگیری میکند. هیچ پاکسازی دستی لازم نیست.
function AutoFocusInput() {
const inputRef = useRef(null);
useEffect(() => {
// فوکوس input پس از mount شدن کامپوننت
inputRef.current?.focus();
}, []);
return <input ref={inputRef} type="text" />;
}
طبق React — Refs and DOM documentation (2025)، از ref برای عملیات اعلامی استفاده نکنید — باز/بستن modalها، مدیریت دید. برای این اهداف state و props وجود دارند. refها برای عملیات امری که نمیتوان به صورت اعلامی بیان کرد طراحی شدهاند: فوکوس، انتخاب متن، ادغام با کتابخانههای شخص ثالث.
دومین سناریوی مهم — ذخیره مقادیر دلخواه که باید بین رندرها حفظ شوند، اما تغییر آنها نباید باعث رندر مجدد شود. مثالهای معمول: شناسه تایمرها (setInterval/setTimeout)، پرچمهایی برای لغو درخواستها، مقادیر قبلی props برای مقایسه و هر دادهای که بر خروجی UI تأثیر نمیگذارد.
این به خصوص در useEffect مفید است: useRef شناسه تایمر را ذخیره میکند و cleanup آن را پاک میکند. اگر timerId در useState ذخیره شود، هر بار setTimerId فراخوانی شود باعث رندر مجدد غیرضروری میشود که نه برای منطق و نه برای UI لازم نیست. useRef این مشکل را بدون سربار و بدون بازکشیدن غیرضروری حل میکند.
function Timer() {
const intervalRef = useRef(null);
const start = () => {
intervalRef.current = setInterval(() => {
console.log('تیک');
}, 1000);
};
const stop = () => {
clearInterval(intervalRef.current);
};
useEffect(() => stop, []);
return <><button onClick={start}>Start</button><button onClick={stop}>Stop</button></>;
}
دilemma اصلی — انتخاب بین useRef و useState — با یک سؤال حل میشود: «آیا با تغییر مقدار نیاز به رندر مجدد هست؟». اگر بله — useState. اگر خیر — useRef. useState حالتی را ذخیره میکند که بر خروجی کامپوننت تأثیر میگذارد؛ useRef دادههایی را ذخیره میکند که برای منطق داخلی لازم هستند اما بر UI تأثیر نمیگذارند.
در عمل، توسعهدهندگان اغلب از useRef برای ذخیره callbackها استفاده میکنند تا از مشکلات closure جلوگیری کنند. مثلاً، اگر useEffect روی یک رویداد مشترک شود و داخل callback به state بهروز نیاز باشد — callback را در useRef ذخیره کنید. در هر رندر ref.current را با تابع جدید بهروز کنید و اثر همیشه callback تازه را بدون اشتراک مجدد فراخوانی میکند.
| معیار | useRef | useState |
|---|---|---|
| رندر مجدد | با تغییر ایجاد نمیکند | با هر setState ایجاد میکند |
| تغییر | مستقیم: ref.current = value | از طریق setter: setState(value) |
| استفاده در JSX | استفاده نمیشود (بدون اثر) | در خروجی کامپوننت استفاده میشود |
| مثال | تایمرها، ارجاعهای DOM، مقادیر قبلی | دادههای فرم، وضعیت UI، پرچمها |
یک ضدالگو وجود دارد: استفاده از useRef برای ذخیره دادههایی که در JSX لازم هستند اما تغییر آنها نباید باعث رندر مجدد شود. این منجر به عدم همگامسازی میشود — UI دادههای قدیمی را نشان میدهد در حالی که ref.current جدید است. اگر مقدار در UI نمایش داده میشود — از useState استفاده کنید. اگر فقط در منطق داخلی استفاده میشود — useRef.
ترکیب useRef + useEffect — الگوی استاندارد برای ردیابی مقادیر قبلی props است. مقدار قبلی را در ref ذخیره کنید، با مقدار فعلی در useEffect مقایسه کنید و بر اساس تفاوت تصمیم بگیرید. این به خصوص در انیمیشنها مفید است، زمانی که باید بدانید چه مقداری قبل از تغییر وجود داشته است.
function PriceDisplay({ price }) {
const prevPriceRef = useRef(price);
useEffect(() => {
const prevPrice = prevPriceRef.current;
if (price > prevPrice) {
animateUp();
} else if (price < prevPrice) {
animateDown();
}
prevPriceRef.current = price;
}, [price]);
return <span>${price}</span>;
}
طبق React Documentation — Hooks FAQ (2025)، این الگو «previous value pattern» نامیده میشود. کار میکند زیرا useRef مقدار را بین رندرها حفظ میکند و useEffect بعد از تأیید تغییرات اجرا میشود. ابتدا DOM با price جدید بهروز میشود، سپس useEffect با قبلی (که هنوز در ref است) مقایسه میکند و ref.current را به مقدار فعلی بهروز میکند.
رایجترین اشتباه — خواندن ref.current در فاز رندر برای محاسبه JSX. از آنجا که تغییر ref.current باعث رندر مجدد نمیشود، کامپوننت ممکن است از مقدار قدیمی استفاده کند. اگر مقدار .current در خروجی UI شرکت میکند — از useState استفاده کنید. اگر نیاز به همگامسازی ref و state دارید، از useEffect برای بهروزرسانی state از ref استفاده کنید.
// ❌ ref.current را در طول رندر برای نمایش نخوانید
function BadComponent() {
const valRef = useRef(0);
return <p>{valRef.current}</p>; // در تغییر بهروز نمیشود
}
// ✅ برای نمایش از state، برای منطق از ref استفاده کنید
function GoodComponent() {
const [val, setVal] = useState(0);
const valRef = useRef(0);
return <p>{val}</p>;
}
یک اشتباه رایج دیگر — استفاده از useRef به عنوان تنها روش ذخیره حالت در کامپوننت، زمانی که دادهها بر UI تأثیر میگذارند. توسعهدهندگان useRef را انتخاب میکنند تا از رندرهای مجدد «غیرضروری» جلوگیری کنند، اما در نهایت UI دریافت میکنند که بهروز نمیشود. رویکرد صحیح: برای دادههای UI از useState و فقط برای مقادیر کمکی که در رندر شرکت نمیکنند از useRef استفاده کنید.
سوالات متداول
بله، این یک الگوی رایج است — previous state pattern. یک ref ایجاد کنید و آن را در useEffect با هر تغییر مقدار مورد نظر بهروز کنید. در لحظه بین رندر و اثر، ref.current حاوی مقدار قبلی است که میتوان با مقدار فعلی مقایسه کرد. این نیاز به رندرهای مجدد اضافی ندارد و با هر نوع دادهای کار میکند.
React مقدار ref.current را فقط پس از رندر شدن عنصر و اضافه شدن به DOM واقعی به گره DOM اختصاص میدهد. در طول رندر اول، کامپوننت هنوز mount نشده است، بنابراین ref.current برابر مقدار اولیه (null) است. دسترسی به DOM از طریق ref.current فقط در useEffect یا در handlerهای رویدادی که بعد از mount فراخوانی میشوند ممکن است.
createRef یک شی ref جدید در هر رندر ایجاد میکند — باید فقط در کامپوننتهای کلاسی استفاده شود. useRef ref را یک بار ایجاد میکند و همان شی را در همه رندرهای بعدی برمیگرداند. در کامپوننتهای تابعی منحصراً از useRef استفاده کنید؛ createRef منجر به از دست رفتن مقدار در رندر مجدد میشود زیرا شی دوباره ایجاد میشود.
بله، شی ref میتواند به عنوان یک prop معمولی منتقل شود: <Child inputRef={inputRef} />. کامپوننت فرزند آن را از طریق inputRef.current استفاده میکند. با این حال، برای انتقال ref مستقیم به عنصر DOM کامپوننت فرزند از forwardRef استفاده کنید — HOC که اجازه میدهد ref از طریق props عبور کند. بدون forwardRef ref به طور خودکار به عنصر DOM داخل کامپوننت فرزند متصل نمیشود.
ref را به عنصر متصل کنید، سپس در useEffect ref.current.getBoundingClientRect() یا ref.current.offsetWidth / offsetHeight را بخوانید. برای ردیابی واکنشی تغییرات اندازه از ResizeObserver درون useEffect استفاده کنید: observer ایجاد کنید، در تغییرات مشترک شوید و state را با اندازههای جدید بهروز کنید. فراموش نکنید observer را در cleanup غیرفعال کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید