useRef — این چیست، hook ارجاع و کار با DOM در React

نویسنده: IT Sectr منتشر شده: 2026-07-04 زمان مطالعه: 9 دقیقه

useRef — یک hook React است که یک شی ref قابل تغییر با ویژگی .current ایجاد می‌کند که بین رندرهای کامپوننت حفظ می‌شود. برخلاف useState، تغییر .current باعث رندر مجدد نمی‌شود، که useRef را برای ذخیره ارجاع‌های DOM، تایمرها و هر مقداری که باید بدون بازکشیدن UI از رندرها عبور کند، ایده‌آل می‌سازد. طبق React Documentation (2025)، شی ref یک بار در طول عمر کامپوننت ایجاد می‌شود و بین رندرها تغییر نمی‌کند که پایداری ارجاع را تضمین می‌کند.

نکات اصلی

  • useRef — hook برای ایجاد شی ref قابل تغییر با فیلد .current.
  • بدون رندر مجدد — تغییر .current باعث بازکشیدن کامپوننت نمی‌شود.
  • ارجاع‌های DOM — به prop عنصر برای دسترسی مستقیم به گره DOM منتقل می‌شود.
  • ذخیره مقادیر — مناسب برای تایمرها، مقادیر قبلی و هر داده بین رندرها.
  • ارجاع پایدار — شی ref برخلاف closureها بین رندرها تغییر نمی‌کند.

useRef در React چیست

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 مدیریت می‌شود.

jsx
import { useRef } from 'react';

function Component() {
    const countRef = useRef(0);
    // countRef.current = 0 در ابتدا
    // countRef.current = 5 پس از تغییر
    // رندر مجدد رخ نمی‌دهد!
}

دسترسی به عناصر DOM از طریق useRef

رایج‌ترین سناریوی استفاده از useRef — دسترسی مستقیم به عنصر DOM است. React بعد از mount شدن کامپوننت، elementRef.current را به ارجاع گره DOM اختصاص می‌دهد. این برای فوکوس کردن input، اندازه‌گیری ابعاد عنصر، ادغام با کتابخانه‌های انیمیشن و کتابخانه‌هایی که از رندر React استفاده نمی‌کنند ضروری است.

React به طور خودکار ارجاع‌های ref به DOM را مدیریت می‌کند: هنگام mount گره DOM واقعی را اختصاص می‌دهد، هنگام unmount آن را صفر می‌کند (null قرار می‌دهد). این تضمین می‌کند که .current همیشه مقدار به‌روز دارد و از نشت حافظه از طریق ارجاع‌های «آویزان» به عناصر حذف شده جلوگیری می‌کند. هیچ پاک‌سازی دستی لازم نیست.

jsx
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 این مشکل را بدون سربار و بدون بازکشیدن غیرضروری حل می‌کند.

jsx
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></>;
}

useRef در مقابل useState: چه زمانی از کدام استفاده کنیم

دilemma اصلی — انتخاب بین useRef و useState — با یک سؤال حل می‌شود: «آیا با تغییر مقدار نیاز به رندر مجدد هست؟». اگر بله — useState. اگر خیر — useRef. useState حالتی را ذخیره می‌کند که بر خروجی کامپوننت تأثیر می‌گذارد؛ useRef داده‌هایی را ذخیره می‌کند که برای منطق داخلی لازم هستند اما بر UI تأثیر نمی‌گذارند.

در عمل، توسعه‌دهندگان اغلب از useRef برای ذخیره callbackها استفاده می‌کنند تا از مشکلات closure جلوگیری کنند. مثلاً، اگر useEffect روی یک رویداد مشترک شود و داخل callback به state به‌روز نیاز باشد — callback را در useRef ذخیره کنید. در هر رندر ref.current را با تابع جدید به‌روز کنید و اثر همیشه callback تازه را بدون اشتراک مجدد فراخوانی می‌کند.

معیارuseRefuseState
رندر مجددبا تغییر ایجاد نمی‌کندبا هر setState ایجاد می‌کند
تغییرمستقیم: ref.current = valueاز طریق setter: setState(value)
استفاده در JSXاستفاده نمی‌شود (بدون اثر)در خروجی کامپوننت استفاده می‌شود
مثالتایمرها، ارجاع‌های DOM، مقادیر قبلیداده‌های فرم، وضعیت UI، پرچم‌ها

یک ضدالگو وجود دارد: استفاده از useRef برای ذخیره داده‌هایی که در JSX لازم هستند اما تغییر آنها نباید باعث رندر مجدد شود. این منجر به عدم همگام‌سازی می‌شود — UI داده‌های قدیمی را نشان می‌دهد در حالی که ref.current جدید است. اگر مقدار در UI نمایش داده می‌شود — از useState استفاده کنید. اگر فقط در منطق داخلی استفاده می‌شود — useRef.

useRef در ترکیب با useEffect

ترکیب useRef + useEffect — الگوی استاندارد برای ردیابی مقادیر قبلی props است. مقدار قبلی را در ref ذخیره کنید، با مقدار فعلی در useEffect مقایسه کنید و بر اساس تفاوت تصمیم بگیرید. این به خصوص در انیمیشن‌ها مفید است، زمانی که باید بدانید چه مقداری قبل از تغییر وجود داشته است.

jsx
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 را به مقدار فعلی به‌روز می‌کند.

اشتباهات رایج با useRef

رایج‌ترین اشتباه — خواندن ref.current در فاز رندر برای محاسبه JSX. از آنجا که تغییر ref.current باعث رندر مجدد نمی‌شود، کامپوننت ممکن است از مقدار قدیمی استفاده کند. اگر مقدار .current در خروجی UI شرکت می‌کند — از useState استفاده کنید. اگر نیاز به همگام‌سازی ref و state دارید، از useEffect برای به‌روزرسانی state از ref استفاده کنید.

  • Ref در JSX — خواندن ref.current درون تابع رندر منجر به عدم همگام‌سازی UI و داده‌ها می‌شود.
  • Ref به عنوان وابستگی useEffect — ref.current نباید در آرایه وابستگی‌ها باشد؛ React تغییرات آن را ردیابی نمی‌کند.
  • بررسی null فراموش شده — هنگام unmount شدن ref.current null می‌شود؛ قبل از دسترسی بررسی کنید.
  • ذخیره توابع در ref — ref.current را در هر رندر به‌روز کنید اگر از الگوی callback تازه استفاده می‌کنید.
jsx
// ❌ 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 استفاده کنید.

سوالات متداول

آیا می‌توان از useRef برای ذخیره حالت قبلی استفاده کرد؟

بله، این یک الگوی رایج است — previous state pattern. یک ref ایجاد کنید و آن را در useEffect با هر تغییر مقدار مورد نظر به‌روز کنید. در لحظه بین رندر و اثر، ref.current حاوی مقدار قبلی است که می‌توان با مقدار فعلی مقایسه کرد. این نیاز به رندرهای مجدد اضافی ندارد و با هر نوع داده‌ای کار می‌کند.

چرا ref.current در رندر اول null است؟

React مقدار ref.current را فقط پس از رندر شدن عنصر و اضافه شدن به DOM واقعی به گره DOM اختصاص می‌دهد. در طول رندر اول، کامپوننت هنوز mount نشده است، بنابراین ref.current برابر مقدار اولیه (null) است. دسترسی به DOM از طریق ref.current فقط در useEffect یا در handlerهای رویدادی که بعد از mount فراخوانی می‌شوند ممکن است.

تفاوت بین useRef و createRef چیست؟

createRef یک شی ref جدید در هر رندر ایجاد می‌کند — باید فقط در کامپوننت‌های کلاسی استفاده شود. useRef ref را یک بار ایجاد می‌کند و همان شی را در همه رندرهای بعدی برمی‌گرداند. در کامپوننت‌های تابعی منحصراً از useRef استفاده کنید؛ createRef منجر به از دست رفتن مقدار در رندر مجدد می‌شود زیرا شی دوباره ایجاد می‌شود.

آیا می‌توان useRef را از طریق props منتقل کرد؟

بله، شی ref می‌تواند به عنوان یک prop معمولی منتقل شود: <Child inputRef={inputRef} />. کامپوننت فرزند آن را از طریق inputRef.current استفاده می‌کند. با این حال، برای انتقال ref مستقیم به عنصر DOM کامپوننت فرزند از forwardRef استفاده کنید — HOC که اجازه می‌دهد ref از طریق props عبور کند. بدون forwardRef ref به طور خودکار به عنصر DOM داخل کامپوننت فرزند متصل نمی‌شود.

چگونه با useRef اندازه عنصر را اندازه‌گیری کنیم؟

ref را به عنصر متصل کنید، سپس در useEffect ref.current.getBoundingClientRect() یا ref.current.offsetWidth / offsetHeight را بخوانید. برای ردیابی واکنشی تغییرات اندازه از ResizeObserver درون useEffect استفاده کنید: observer ایجاد کنید، در تغییرات مشترک شوید و state را با اندازه‌های جدید به‌روز کنید. فراموش نکنید observer را در cleanup غیرفعال کنید.

خلاصه

  • useRef — hook برای ایجاد شی ref قابل تغییر که بین رندرها بدون ایجاد رندر مجدد حفظ می‌شود.
  • دسترسی به DOM — prop ref برای دسترسی مستقیم به گره DOM به عنصر منتقل می‌شود؛ بعد از mount در دسترس است.
  • ذخیره داده‌ها — تایمرها، callbackها، پرچم‌ها و هر مقداری که بر UI تأثیر نمی‌گذارد در ref ذخیره کنید.
  • useRef در مقابل useState — اگر تغییر باید UI را بازکشد useState، اگر نه useRef انتخاب کنید.
  • Previous value pattern — مقدار قبلی را در ref ذخیره کنید، برای مقایسه در useEffect به‌روز کنید.
  • Ref در JSX — ref.current را در فاز رندر نخوانید؛ این منجر به عدم همگام‌سازی UI و داده‌ها می‌شود.
  • forwardRef — برای انتقال ref از طریق props به کامپوننت فرزند استفاده کنید.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید