مفاهيم دورة الحياة للذاكرة والوصول لعناصر الـ DOM عبر useRef
| وجه المقارنة | المتغير عبر useState | المتغير عبر useRef |
|---|---|---|
| إعادة رندرة المكون (Re-render) | يؤدي كل تحديث للقيمة إلى إعادة رسم المكون فوراً | لا يسبب أي إعادة رندرة للمكون نهائياً عند تغيير قيمته |
| الاحتفاظ بالقيمة بين الرندرات | نعم يمتد عبر الرندرات المختلفة | نعم يمتد ويحافظ على قيمته طوال فترة حياة المكون |
| وصول مباشر لعناصر الـ DOM | لا (يتم التعامل مع الواجهة من خلال React Virtual DOM) | نعم (يتيح إسناد مرجع مباشر لعنصر HTML حقيقي) |
| التعديل اللحظي (Synchronous) | يتغير بحسب دورة التحديث القادمة (Asynchronous) | يتغير فوراً وتكون القيمة الجديدة متاحة في السطر التالي مباشرة |
عندما نغير قيمة أي متغير معرف بـ useState، تعيد React تشغيل المكون ورسمه من جديد (Re-render). ولكن هناك حالات في الهندسة البرمجية نحتاج فيها إلى:
ℹ️ معلومة
هنا يأتي دور useRef، وهو عبارة عن هوك يمنحك كائناً محمولاً يحتوي على الخاصية الوحيدة current، وتستطيع تعديله بحرية كاملة دون إزعاج React بالرندرة!
في حالات معينة (مثل التركيز Focus، التمرير Scroll، أو قياس أبعاد عنصر)، نحتاج للتعامل مع عنصر الـ DOM مباشرة.
| 1 | import { useRef } from 'react'; |
| 2 | |
| 3 | function SearchInput() { |
| 4 | const inputRef = useRef(null); |
| 5 | |
| 6 | const handleFocus = () => { |
| 7 | // الوصول المباشر لعنصر الـ HTML والتركيز عليه |
| 8 | inputRef.current.focus(); |
| 9 | }; |
| 10 | |
| 11 | return ( |
| 12 | <div> |
| 13 | <input ref={inputRef} type="text" placeholder="ابحث عن كورس..." /> |
| 14 | <button onClick={handleFocus}>تركيز على مربع البحث</button> |
| 15 | </div> |
| 16 | ); |
| 17 | } |
إذا أردنا تتبع قيمة ما عبر الزمن (مثل معرّف مؤقت زمني timerId أو عدد مرات الضغط لتجميع البيانات)، فإن استخدام useState سيعيد رسم الصفحة بلا داعٍ مع كل حركة.
| 1 | import { useState, useRef, useEffect } from 'react'; |
| 2 | |
| 3 | function Timer() { |
| 4 | const [seconds, setSeconds] = useState(0); |
| 5 | const timerId = useRef(null); // تخزين معرّف المؤقت دون التسبب في رندرة |
| 6 | |
| 7 | const startTimer = () => { |
| 8 | if (timerId.current !== null) return; |
| 9 | timerId.current = setInterval(() => { |
| 10 | setSeconds((prev) => prev + 1); |
| 11 | }, 1000); |
| 12 | }; |
| 13 | |
| 14 | const stopTimer = () => { |
| 15 | clearInterval(timerId.current); |
| 16 | timerId.current = null; // إعادة تصفير المرجع |
| 17 | }; |
| 18 | |
| 19 | return ( |
| 20 | <div> |
| 21 | <h3>العداد: {seconds} ثانية</h3> |
| 22 | <button onClick={startTimer}>تشغيل</button> |
| 23 | <button onClick={stopTimer}>إيقاف</button> |
| 24 | </div> |
| 25 | ); |
| 26 | } |
طريقة رائعة لتتبع كفاءة تطبيقك بدون إشعال حلقة تكرارية:
| 1 | function RenderTracker() { |
| 2 | const renderCount = useRef(1); |
| 3 | |
| 4 | useEffect(() => { |
| 5 | renderCount.current += 1; |
| 6 | console.log(`تم إعادة رندرة هذا المكون ${renderCount.current} مرات`); |
| 7 | }); |
| 8 | |
| 9 | return <div>مكون متابعة الأداء</div>; |
| 10 | } |
⚠️ تحذير
تجنب تعديل واجهة المستخدم عبر الـ DOM المباشر باستخدام useRef! لا تستخدم useRef لتغيير النصوص أو الألوان أو إخفاء العناصر يدوياً بالشكل التقليدي:
| 1 | // ❌ خطأ هندسي يخل بفلسفة ريأكت |
| 2 | myRef.current.style.display = 'none'; |
عندما تعدل الـ DOM يدوياً مباشرة بعيداً عن React Virtual DOM، تجعل React تعيش في معزل عن الوضع الحقيقي للشاشة، مما يسبب تضارباً غير متوقع في الرندرة القادمة!