Kodokon kodokon.com

useRef والتلاعب بالـ DOM

تلاعب بالـ DOM واحتفظ بقيم النسخة باستخدام useRef، دون كسر نقاء التصيير.

7 دقيقة · 3 أسئلة

افتح هذا الدرس في Kodokon

يُرجع useRef صندوقًا قابلاً للتغيير - كائنًا بخاصية واحدة هي current - تبقى هويته عبر كل تصيير. فرقان جوهريان عن الحالة: تعديل ref.current لا يُطلق أي إعادة تصيير، والتغيير مرئي فورًا، دون انتظار التصيير التالي. من ثمّ عائلتان من الاستخدام: الإشارة إلى عقدة DOM، وتخزين قيمة نسخة يجب أن تبقى عبر عمليات التصيير دون أن تقود العرض.

JSX
import { useEffect, useRef } from "react";

function SearchField() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current.focus();
  }, []);

  return <input ref={inputRef} type="search" />;
}
تربط React العقدة بالـ ref بعد الالتزام بالتصيير (commit).

في جانب قيمة النسخة، كل ما يجب أن يبقى دون التسبب في تصيير ينتمي إلى ref: المعرّف الذي يُرجعه setInterval، أو نسخة مكتبة خارجية (خريطة، محرر، مشغّل فيديو)، أو قيمة من تصيير سابق. القاعدة الذهبية تبقى نفسها: يجب أن يكون التصيير دالة نقية للـ props والحالة. تُقرأ الـ refs وتُكتب في التأثيرات ومعالجات الأحداث، لا أثناء التصيير.

JAVASCRIPT
import { useEffect, useRef } from "react";

function usePrevious(value) {
  const ref = useRef(undefined);

  useEffect(() => {
    ref.current = value;
  }, [value]);

  return ref.current;
}
يُشغَّل التأثير بعد التصيير: يبقى الـ ref متأخرًا بتصيير واحد.

لكشف عقدة DOM من مكوّنك الخاص، مرّر الـ ref: منذ React 19، يُعدّ ref prop عادية للمكوّنات الدالّية؛ وقبل ذلك، كان المرور عبر forwardRef إلزاميًا. رد فعل أخير ينبغي تنميته: فضّل النهج التصريحي عندما تكفي الحالة لوصف النتيجة (صنف CSS، سمة disabled). احتفظ بالـ refs للمجال الذي لا تغطيه React: التركيز، تحديد النص، قياس عنصر، التمرير، تشغيل الوسائط.

JSX
import { useRef } from "react";

function TextField({ ref, ...props }) {
  return <input ref={ref} {...props} />;
}

function Form() {
  const fieldRef = useRef(null);

  return (
    <>
      <TextField ref={fieldRef} />
      <button onClick={() => fieldRef.current.focus()}>
        Give focus
      </button>
    </>
  );
}
منذ React 19، يُمرَّر ref مثل أي prop أخرى.

اختبار المعرفة

تأكّد من أنك تذكّرت النقاط الأساسية في هذا الدرس.

  1. ما الفرق الجوهري الذي يفصل useRef عن useState لتخزين قيمة؟
    • تعديل ref.current لا يُطلق أي إعادة تصيير، بخلاف تحديث الحالة
    • يحتفظ useRef بقيمته بعد تفكيك المكوّن
    • لا يمكن لـ useRef تخزين سوى عقد DOM
    • تُشارَك الـ refs بين جميع نسخ المكوّن
  2. لماذا نصل إلى inputRef.current في useEffect بدلاً من أثناء التصيير؟
    • لأن useEffect يجمّع الوصول إلى الـ DOM لأداء أفضل
    • لأن الـ refs مقفلة للقراءة أثناء التصيير
    • لأن React لا تربط العقدة إلا بعد الالتزام (commit): أثناء التصيير الأول، يكون current لا يزال null
  3. ماذا يُرجع usePrevious(value) في أول تصيير على الإطلاق؟
    • القيمة الحالية لـ value
    • undefined، لأن التأثير الذي يكتب في الـ ref لم يُشغَّل بعد
    • null
    • قيمة التصيير التالي