useRef هو خطاف React يُنشئ كائن ref قابل للتغيير بخاصية .current، يُحفظ بين تصييرات المكون. على عكس useState، تغيير .current لا يُسبب إعادة تصيير، مما يجعل useRef مثالياً لتخزين مراجع DOM، الموقتات وأي قيم يجب أن تستمر عبر التصييرات دون إعادة رسم واجهة المستخدم. وفقاً لوثائق React (2025)، يتم إنشاء كائن ref مرة واحدة خلال عمر المكون ولا يتغير بين التصييرات، مما يضمن استقرار المرجع.
الخلاصة
useRef هو خطاف أُضيف في React 16.8 يُعيد كائن ref قابل للتغيير بخاصية واحدة .current. يتم تعيين القيمة الأولية إلى .current عند تركيب المكون. الفرق الرئيسي عن المتغير العادي هو أن كائن ref يبقى عبر إعادة التصيير: في التصييرات اللاحقة، لا يُعيد React إنشاء ref بل يُعيد نفس الكائن.
داخلياً، يستخدم useRef نفس آلية useState و useEffect — قائمة مرتبطة من الخطافات على عقدة fiber. ومع ذلك، على عكس useState، لا يتتبع React تغييرات كائن ref، ولا يُدرج التحديثات في قائمة الانتظار ولا يُخطط لإعادة التصيير. وهذا يجعل useRef خطافاً خفيفاً جداً بدون تأثير على الأداء حتى مع التغييرات المتكررة.
وفقاً لفريق React — الأسئلة الشائعة عن الخطافات (2025)، useRef هو في الأساس «صندوق» يخزن قيمة. سكر نحوي: const ref = useRef(قيمة_ابتدائية) يكافئ const ref = { current: قيمة_ابتدائية } مع فارق أن React يضمن أن كائن ref سيكون نفسه في كل تصيير. لا سحر — مجرد مرجع ثابت يُدار بواسطة React.
import { useRef } from 'react';
function Component() {
const countRef = useRef(0);
// countRef.current = 0 في البداية
// countRef.current = 5 بعد التغيير
// لا يحدث إعادة تصيير!
}
حالة الاستخدام الأكثر شيوعاً لـ useRef هي الحصول على وصول مباشر إلى عنصر DOM. يُخصص React لـ elementRef.current مرجعاً لعقدة DOM بعد تركيب المكون. هذا ضروري لتركيز إدخال، قياس أبعاد العنصر، التكامل مع مكتبات الرسوم المتحركة والمكتبات التي لا تستخدم تصيير React.
يدير React تلقائياً مراجع ref على DOM: عند التركيب يُخصص عقدة DOM الفعلية، وعند إلغاء التركيب يُعيدها إلى null. هذا يضمن أن .current يحمل دائماً القيمة المحدثة ويمنع تسرب الذاكرة عبر المراجع المعلقة للعناصر المحذوفة. لا حاجة للتنظيف اليدوي.
function AutoFocusInput() {
const inputRef = useRef(null);
useEffect(() => {
// تركيز الإدخال بعد تركيب المكون
inputRef.current?.focus();
}, []);
return <input ref={inputRef} type="text" />;
}
وفقاً لـ React — وثائق Refs و DOM (2025)، لا تستخدم ref للعمليات التصريحية — فتح/إغلاق النوافذ المنبثقة، إدارة الرؤية. لهذه الأغراض يوجد state و props. الـ Refs مخصصة للعمليات الأمرية التي لا يمكن التعبير عنها بشكل تصريحي: التركيز، تحديد النص، التكامل مع مكتبات الطرف الثالث.
حالة الاستخدام الثانية من حيث الأهمية هي تخزين القيم العشوائية التي يجب أن تستمر بين التصييرات ولكن تغييرها لا يجب أن يُسبب إعادة تصيير. أمثلة نموذجية: معرفات الموقتات (setInterval/setTimeout)، علامات إلغاء الطلبات، قيم props السابقة للمقارنة وأي بيانات لا تؤثر على مخرجات واجهة المستخدم.
هذا مفيد بشكل خاص في useEffect: useRef يخزن معرف الموقت و cleanup يمسحه. إذا تم تخزين timerId في useState، كل استدعاء لـ setTimerId سيُسبب إعادة تصيير غير ضرورية ليست مطلوبة لا للمنطق ولا لواجهة المستخدم. 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></>;
}
المعضلة الرئيسية — الاختيار بين useRef و useState — تُحل بسؤال واحد: «هل هناك حاجة لإعادة تصيير عند تغيير القيمة؟». إذا نعم — useState. إذا لا — useRef. useState يخزن الحالة التي تؤثر على مخرجات المكون؛ useRef يخزن البيانات اللازمة للمنطق الداخلي ولكنها لا تؤثر على واجهة المستخدم.
عملياً، يستخدم المطورون غالباً useRef لتخزين دوال الاسترجاع لتجنب مشاكل الإغلاق. على سبيل المثال، إذا اشترك useEffect في حدث وكانت دالة الاسترجاع تحتاج إلى الحالة الحالية — خزّن دالة الاسترجاع في useRef. في كل تصيير، حدّث ref.current بدالة جديدة، وسيستدعي التأثير دائماً دالة الاسترجاع الجديدة دون إعادة اشتراك.
| المعيار | useRef | useState |
|---|---|---|
| إعادة التصيير | لا يُسبب عند التغيير | يُسبب في كل setState |
| التغيير | مباشر: ref.current = قيمة | عبر setter: setState(قيمة) |
| الاستخدام في JSX | لا يُستخدم (بدون تأثير) | يُستخدم في مخرجات المكون |
| مثال | موقتات، مراجع DOM، قيم سابقة | بيانات نموذج، حالة واجهة المستخدم، علامات |
يوجد نمط مضاد: استخدام useRef للبيانات المطلوبة في JSX ولكن تغييرها لا يجب أن يُسبب إعادة تصيير. هذا يؤدي إلى عدم التزامن — تُظهر واجهة المستخدم بيانات قديمة بينما ref.current جديد بالفعل. إذا كانت القيمة تُعرض في واجهة المستخدم — استخدم 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 — الأسئلة الشائعة عن الخطافات (2025)، يُسمى هذا النمط «نمط القيمة السابقة». يعمل لأن useRef يحفظ القيمة بين التصييرات، ويتم تنفيذ useEffect بعد تأكيد التغييرات. أولاً يتم تحديث DOM بالسعر الجديد، ثم يقارنه useEffect بالسابق (الذي لا يزال في ref) ويُحدّث ref.current إلى القيمة الحالية.
الخطأ الأكثر شيوعاً هو قراءة ref.current خلال مرحلة التصيير لحساب JSX. نظراً لأن تغيير ref.current لا يُسبب إعادة تصيير، قد يستخدم المكون قيمة قديمة. إذا كان .current يشارك في مخرجات واجهة المستخدم — استخدم 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 كطريقة وحيدة لتخزين الحالة في مكون عندما تؤثر البيانات فعلياً على واجهة المستخدم. يختار المطورون useRef لتجنب إعادة التصيير «غير الضرورية»، لكنهم ينتهي بهم الأمر بواجهة مستخدم لا تتحدّث. النهج الصحيح: استخدم useState لبيانات واجهة المستخدم و useRef فقط للقيم المساعدة غير المشاركة في التصيير.
الأسئلة الشائعة
نعم، هذا نمط شائع — نمط الحالة السابقة. أنشئ ref وحدّثه في useEffect في كل مرة تتغير فيها القيمة المتتبعة. بين التصيير والتأثير، يحتوي ref.current على القيمة السابقة التي يمكن مقارنتها بالحالية. هذا لا يتطلب إعادة تصيير إضافية ويعمل مع أي نوع بيانات.
يُخصص React ref.current قيمة عقدة DOM فقط بعد أن يتم تصيير العنصر وإضافته إلى DOM الحقيقي. أثناء التصيير الأول، المكون ليس مركباً بعد، لذلك ref.current يساوي القيمة الأولية (null). الوصول إلى DOM عبر ref.current ممكن فقط في useEffect أو في معالجات الأحداث التي تُستدعى بعد التركيب.
createRef يُنشئ كائن ref جديد في كل تصيير — يجب استخدامه فقط في مكونات الفئة. useRef يُنشئ ref مرة واحدة ويُعيد نفس الكائن في جميع التصييرات اللاحقة. في المكونات الدالية، استخدم حصراً useRef؛ createRef سيؤدي إلى فقدان القيمة عند إعادة التصيير لأن الكائن سيُعاد إنشاؤه.
نعم، يمكن تمرير كائن ref كـ prop عادي: <Child inputRef={inputRef} />. المكون الابن يستخدمه عبر inputRef.current. ومع ذلك، لتمرير ref مباشرة إلى عنصر DOM لمكون ابن، استخدم forwardRef — HOC الذي يسمح بإعادة توجيه refs عبر props. بدون forwardRef، لن يتم ربط ref تلقائياً بعنصر DOM داخل المكون الابن.
اربط ref بالعنصر، ثم في useEffect اقرأ ref.current.getBoundingClientRect() أو ref.current.offsetWidth / offsetHeight. للتتبع التفاعلي لتغييرات الحجم، استخدم ResizeObserver داخل useEffect: أنشئ observer، اشترك في التغييرات وحدّث state بالأبعاد الجديدة. لا تنسَ فصل observer في الـ cleanup.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.