يُرجع useRef كائنًا، { current: initialValue }، تحتفظ به React طوال عمر المكوّن. تغيير ref.current لا يعيد عرض أي شيء. وهذا يجعل المراجع مناسبة لمهمتين: حمل عقدة DOM لتستدعي عليها توابع مثل focus()، وتذكّر قيمة بين مرات العرض لا تحتاج الشاشة إلى عرضها.
تمرير كائن المرجع إلى ref={inputRef} يخبر React بوضع عقدة DOM الخاصة بـ <input> في inputRef.current. غيّر focus() إلى select()، واكتب شيئًا، وانقر مرة أخرى: يُظلَّل النص.
الصيغة
const myRef = useRef(initialValue);
myRef.current; // read
myRef.current = value; // write, no re-render
- تُستخدم
initialValueفي العرض الأول فقط. وفي مراجع DOM تكونnull. - تُرجع React الكائن نفسه في كل عرض. ولا يتغير إلا
current. - قراءة
currentوالكتابة فيها JavaScript عادية. لا تتتبعها React.
قيمة لا تسبب العرض
الحالة لما تعرضه الشاشة. والمرجع لما يحتاج المكوّن إلى تذكّره دون عرضه. تحتفظ هذه الكتلة بعدّاد في مرجع وآخر في الحالة، وتسجّل الاثنين:
انقر Ref + 1 بضع مرات. تُظهر وحدة التحكم ref.current يزداد، لكن الصفحة ما زالت تقول 0 ولا يُسجَّل أي عرض. انقر الآن State + 1: يُعرض المكوّن، ويقفز عدّاد المرجع على الشاشة إلى الرقم الذي كان المرجع يحمله أصلًا. احتفظ المرجع بقيمته طوال الوقت؛ لكن React لم يكن لديها سبب لرسمه.
المراجع مقابل الحالة
useState | useRef | |
|---|---|---|
| يُرجع | [value, setValue] | { current: value } |
| تغييره | يعيد عرض المكوّن | لا يفعل شيئًا مرئيًا |
| متى تصبح القيمة الجديدة مقروءة | في العرض التالي | فورًا |
| يُغيَّر عبر | دالة الضبط | الإسناد، ref.current = x |
| يُستخدم لـ | كل ما تعرضه الشاشة | معرّفات المؤقتات وعقد DOM والقيم السابقة والعلامات |
إذا لم تكن متأكدًا، فاسأل هل تقرأ JSX القيمة. إن كانت تقرؤها، فمكانها الحالة.
تخزين معرّف مؤقت
يُرجع setInterval معرّفًا تحتاجه لاحقًا لإيقاف المؤقت. وهو مناسب للمرجع: يحتاجه معالج Stop، لكن الشاشة لا تعرضه أبدًا، والاحتفاظ به في متغير عادي سيفقده في العرض التالي.
استبدل useRef(null) بـ let intervalRef = { current: null }; وجرّب Start، وانتظر ثانيتين، ثم Stop: يستمر المؤقت. كل نبضة تعرض المكوّن، فتنشئ كائنًا جديدًا، فلا يمسح Stop شيئًا.
تذكّر القيمة السابقة
يعمل التأثير بعد العرض، فيستطيع مقارنة القيمة الجديدة بالمخزنة في مرجع، ثم حفظ الجديدة للمرة القادمة:
انقر green ثم blue: تسجّل وحدة التحكم changed from red to green وchanged from green to blue. والنقر على اللون المحدد أصلًا لا يسجّل شيئًا، لأن التأثير لا يعمل إلا عندما يتغير color.
التعامل مع DOM
المراجع هي طريقتك لاستدعاء واجهات المتصفح التي لا prop لها في React: focus() وselect() وscrollIntoView() وplay() على فيديو، أو قراءة حجم بـ getBoundingClientRect().
const lastItemRef = useRef(null);
function scrollToLast() {
lastItemRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
<li ref={lastItemRef}>Last item</li>
تكون ref.current هي null أثناء العرض الأول، لأن React لم تنشئ عقدة DOM بعد، وتعود كذلك بعد إزالة العنصر. اقرأها في معالجات الأحداث والتأثيرات، حيث تكون العقدة موجودة. ولقياس عنصر وتحديد موضع شيء قبل أن يرسم المتصفح، اقرأ المرجع في useLayoutEffect.
لا تقرأ ref.current ولا تكتب فيها أثناء العرض
تتوقع React أن يكون جسم المكوّن حسابًا نقيًا: props وحالة نفسها، JSX نفسها. يتغير المرجع خارج هذا النموذج، فالعرض الذي يقرؤه أو يكتب فيه قد يُظهر نتائج مختلفة للمدخلات نفسها، وقد تستدعي React الجسم أكثر مما تتوقع.
function Bad() {
const count = useRef(0);
count.current++; // writing during render
return <p>{count.current}</p>; // reading during render
}
اقرأ المراجع واكتب فيها في معالجات الأحداث والتأثيرات. والاستثناء الوحيد هو التهيئة الكسولة، حيث تملأ المرجع مرة واحدة في العرض الأول:
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = new VideoPlayer(); // runs once
}
(يقرأ مثال عدّاد المرجع أعلاه clicks.current في JSX عمدًا، ليُظهر أن الشاشة لا تتبعه. لا تنسخ ذلك إلى كود حقيقي.)
دوال المرجع
بدلًا من كائن مرجع، يمكنك تمرير دالة إلى ref. تستدعيها React مع عقدة DOM عندما يُضاف العنصر. وفي React 19 يمكن للدالة أن تُرجع دالة تنظيف، تستدعيها React عندما يُزال العنصر. وهذا مفيد للقوائم، حيث لا يمكنك استدعاء useRef مرة لكل عنصر:
عند التحميل تسجّل وحدة التحكم سطر attached لكل عنصر. انقر Remove last فلا يظهر إلا detached Cheese. الدالة trackNode معرّفة خارج المكوّن، فهي الدالة نفسها في كل عرض وتترك React العناصر الأخرى وشأنها. اكتبها مضمّنة على شكل ref={(node) => { ... }} بدلًا من ذلك فتفصل كل نقرة كل عنصر وتعيد ربطه، لأن كل عرض يمرر دالة جديدة.
تمرير مرجع إلى مكوّنك الخاص
في React 19 تكون ref prop عادية لمكوّنات الدوال. ينشئ الأب المرجع، ويضعه الابن على عنصر DOM الذي يريد كشفه:
قبل React 19 كان هذا يحتاج إلى forwardRef، الذي ما زال يعمل. تُظهر صفحة forwardRef النسختين وكيف تكشف بضعة توابع فقط بـ useImperativeHandle.
أخطاء شائعة
عرض قيمة مرجع في JSX. لا تتبعها الشاشة. إذا كان ينبغي للمستخدم أن يرى القيمة، فاستخدم الحالة.
استخدام ref.current كاعتمادية للتأثير. يقرأ [ref.current] القيمة أثناء العرض ولا تعرف React متى تتغير، فلا يعمل التأثير من جديد عندما تتغير. استخدم دالة مرجع للاستجابة لربط عقدة.
قراءة مرجع DOM أثناء العرض الأول. يكون null حتى تنشئ React العقدة. اقرأه في تأثير أو معالج.
إنشاء كائن جديد بدلًا من استخدام الخطاف. الكائن const ref = { current: null } في جسم المكوّن كائن جديد في كل عرض. وحده useRef يعطيك الكائن نفسه في كل مرة.
الأسئلة الشائعة
ماذا يُرجع useRef؟
كائنًا عاديًا فيه خاصية واحدة، current، مضبوطة على القيمة الابتدائية التي تمررها. تُرجع React الكائن نفسه في كل عرض، فيبقى ما تضعه في current موجودًا في المرة التالية.
ما الفرق بين useRef وuseState؟
تغيير الحالة يعيد عرض المكوّن؛ أما تغيير ref.current فلا. استخدم الحالة للقيم التي تعرضها الشاشة، والمرجع للقيم التي لا تحتاجها إلا معالجات الأحداث والتأثيرات، مثل معرّف مؤقت أو عقدة DOM.
لماذا تكون ref.current قيمتها null؟
تضبط React القيمة ref.current على عقدة DOM بعد أن تنشئ العقدة، أثناء مرحلة الإثبات (commit). أثناء العرض الأول ما زالت null، وتعود null بعد إزالة العنصر. اقرأها في معالج حدث أو تأثير.
هل يسبب تغيير ref.current إعادة عرض؟
لا. لا تعرف React أنك غيّرتها. ولا تظهر القيمة الجديدة على الشاشة إلا إذا أعاد شيء آخر، مثل تحديث للحالة، عرض المكوّن.
كيف أمرر مرجعًا إلى مكوّن ابن؟
في React 19 يستقبل مكوّن الدالة ref كـ prop عادية، فتمرر <MyInput ref={inputRef} /> ويضعه الابن على <input> الخاص به. أما الإصدارات الأقدم فكانت تحتاج إلى forwardRef.