Menu
flag Ar iconالعربيةdown icon

forwardRef في React: تمرير المراجع وref كـ prop في React 19

يتيح forwardRef للمكوّن أن يأخذ مرجعًا من أبيه ويربطه بعقدة DOM بداخله. وفي React 19 تستقبل مكوّنات الدوال ref كـ prop عادية، فلم يعد الكود الجديد يحتاج إلى forwardRef. شاهد النسختين تعملان، مع useImperativeHandle وأنواع TypeScript.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

يتيح forwardRef لمكوّن أب أن يمرر ref عبر مكوّنك إلى عنصر DOM بداخله، فيستطيع الأب استدعاء focus() وscrollIntoView() وتوابع مشابهة على ذلك العنصر. في React 19 لم تعد تحتاج إليه: تستقبل مكوّنات الدوال ref كـ prop عادية. ما زال forwardRef يعمل، وستراه في معظم الكود الموجود، لذا تعرض هذه الصفحة الطريقتين.

يأخذ forwardRef دالة عرض لها وسيطان: props، والمرجع ref الذي مرره الأب. ليست ref داخل props؛ بل تصل وحدها. انقر Focus فتؤكد وحدة التحكم أن inputRef.current هي عقدة <input> الحقيقية.

طريقة React 19: ref هي prop

منذ React 19 يحصل مكوّن الدالة على ref في props مثل أي prop أخرى. لا مغلّف، ولا وسيط ثانٍ:

اكتب شيئًا وانقر Log value: يقرأ الأب النص الحالي للحقل مباشرة من عقدة DOM. احذف ref={ref} من <input> وانقر Focus: تُظهر وحدة التحكم خطأ، لأن inputRef.current أصبحت الآن null.

forwardRef (كل الإصدارات)الـ prop المسماة ref (React 19 وما بعده)
كيف يحصل المكوّن على المرجعالوسيط الثاني، (props, ref)props.ref
هل يحتاج إلى مغلّفنعملا
يعمل مع useImperativeHandleنعمنعم
الوضعيعمل، ومخطط لإهمالهطريقة كتابة الكود الجديد

المرجع الذي يتجاهله الابن يبقى null

تمرير ref إلى مكوّن لا يفعل شيئًا بذاته. يجب أن يضعه المكوّن على عنصر. وإن لم يفعل، تبقى ref.current هي null:

تُظهر وحدة التحكم Forgetful ref: null وCareful ref: INPUT. هذا أشيع سبب لظهور "ref.current is null" على مكوّن مخصص. وقبل React 19 كان Careful سيكون null أيضًا ما لم يُغلَّف بـ forwardRef، لأن المرجع لم يكن يُسلَّم إلى الدالة أبدًا.

useImperativeHandle: اكشف واجهة أصغر

إعطاء الأب عقدة DOM كاملة يعني أنه يستطيع فعل أي شيء بها: تغيير الأنماط، أو حذف الأبناء، أو قراءة قيم أردت إبقاءها خاصة. يتيح لك useImperativeHandle أن تقرر ما تحتويه ref.current بدلًا من ذلك:

يسجّل الزر الأخير ["focus", "clear"]: يحصل الأب على هذين التابعين ولا شيء غيرهما، لا على عقدة الحقل. أضف تابعًا select() إلى الكائن وزرًا يستدعيه، فيكسب الأب قدرة واحدة إضافية بالضبط.

يحتفظ الابن بمرجعه الخاص، inputRef، للحقل الحقيقي، ويغلّفه المقبض. والوسيط الثالث مصفوفة اعتماديات، مثل التي في useEffect: يُعاد بناء المقبض عندما تتغير تلك القيم.

الجأ إلى هذا باعتدال. معظم ما يريده الأب (الفتح، والإغلاق، وعرض خطأ) أفضل كـ props، مثل isOpen أو error. والتوابع الأمرية مخصصة للأفعال التي لا prop طبيعية لها: التركيز، والتمرير، وتشغيل فيديو، وبدء حركة.

استخدام المرجع داخل الابن أيضًا

أحيانًا يحتاج الابن إلى عقدة DOM نفسها لعمله الخاص، مثل قياسها أو تركيزها بعد خطأ، بينما يحمل الأب أيضًا مرجعًا إليها. لا تأخذ السمة ref الواحدة إلا قيمة واحدة، فادمج الاثنين بدالة مرجع:

function AutoGrowTextarea({ ref, ...props }) {
    const localRef = useRef(null);

    function setRefs(node) {
        localRef.current = node;
        if (typeof ref === 'function') ref(node);
        else if (ref) ref.current = node;
    }

    return <textarea ref={setRefs} {...props} />;
}

قد يكون ref الخاص بالأب كائنًا من useRef أو دالة، فتعامل مع الحالتين. وعندما لا يحتاج الأب إلا إلى بضعة أفعال، يكون useImperativeHandle أعلاه الخيار الأنظف، لأن الابن يحتفظ بالعقدة لنفسه.

تمرير مرجع عبر عدة طبقات

ينتقل المرجع مكوّنًا واحدًا في كل مرة. إذا عرض Form المكوّن Field، الذي يعرض TextInput، الذي يعرض <input>، فعلى كل من هذه المكوّنات تمرير ref إلى التالي. في React 19 هذه prop إضافية واحدة تُمرَّر (<TextInput ref={ref} />)؛ أما مع forwardRef فكان يجب تغليف كل طبقة. ونشر props ({...props}) لا يحمله في الإصدارات الأقدم، لأن ref لم تكن جزءًا من props قبل React 19.

مكوّنات أنظمة التصميم هي المكان المعتاد الذي يهم فيه ذلك: ينبغي لمكوّن Button أو Input أو Select يغلّف عنصرًا أصليًا أن يمرر مرجعه، ليتمكن التطبيق الذي يستخدمه من تركيزه، أو قياسه، أو تسليمه لمكتبة تحدد مواضع النوافذ المنبثقة.

الترحيل من forwardRef

التغيير آلي: أزل المغلّف واقرأ ref من props.

// Before
const Button = forwardRef(function Button({ variant, ...props }, ref) {
    return <button ref={ref} className={variant} {...props} />;
});

// After (React 19)
function Button({ variant, ref, ...props }) {
    return <button ref={ref} className={variant} {...props} />;
}

لا عجلة في الأمر. يظل forwardRef يعمل في React 19، والمكتبة التي يجب أن تدعم React 18 عليها الاحتفاظ به، لأن React 18 لا تمرر ref كـ prop. ولا تتأثر مكوّنات الأصناف: المرجع على مكوّن صنف ما زال يشير إلى نسخة المكوّن.

TypeScript

مع forwardRef تأتي وسائط الأنواع بالترتيب: نوع المرجع، ثم props:

import { forwardRef } from 'react';

type FancyInputProps = { label: string };

const FancyInput = forwardRef<HTMLInputElement, FancyInputProps>(
    function FancyInput({ label }, ref) {
        return <input ref={ref} aria-label={label} />;
    }
);

في React 19 حدّد نوع ref مثل أي prop أخرى. يتضمنه ComponentProps<'input'> أصلًا:

import { useImperativeHandle, useRef, type ComponentProps, type Ref } from 'react';

function FancyInput(props: ComponentProps<'input'>) {
    return <input {...props} />;
}

type SearchHandle = { focus: () => void; clear: () => void };

function SearchBox({ ref }: { ref?: Ref<SearchHandle> }) {
    const inputRef = useRef<HTMLInputElement>(null);
    useImperativeHandle(ref, () => ({
        focus: () => inputRef.current?.focus(),
        clear: () => {
            if (inputRef.current) inputRef.current.value = '';
        },
    }));
    return <input ref={inputRef} />;
}

// In the parent
const searchRef = useRef<SearchHandle>(null);

تتناول صفحة useRef المراجع نفسها: الوصول إلى DOM، والقيم التي تبقى دون عرض، ودوال المرجع. ولتحديد أنواع المكوّنات عمومًا، راجع React مع TypeScript.

الأسئلة الشائعة

ماذا يفعل forwardRef في React؟

يغلّف مكوّن دالة بحيث يصل ref الذي يعطيه الأب إلى المكوّن كوسيط ثانٍ، (props, ref). ثم يضع المكوّن ذلك المرجع على عقدة DOM، فيستطيع الأب استدعاء توابع مثل focus() عليها.

هل أُهمل forwardRef في React 19؟

ليس بعد، وما زال يعمل. تمرر React 19 الخاصية ref إلى مكوّنات الدوال كـ prop عادية، فلا يحتاجه الكود الجديد، وقد قال فريق React إنه يخطط لإهمال forwardRef في إصدار قادم.

لماذا يكون مرجعي null على مكوّن مخصص؟

استقبل المكوّن المرجع لكنه لم يضعه على أي عنصر. اربطه بعقدة DOM بداخله: <input ref={ref} />. وقبل React 19 لم يكن المرجع يُمرَّر أصلًا ما لم يُغلَّف المكوّن بـ forwardRef.

فيمَ يُستخدم useImperativeHandle؟

يستبدل ما يراه الأب في ref.current. بدلًا من عقدة DOM كاملة تُرجع كائنًا فيه التوابع التي تختارها فقط، مثل focus وclear.

هل تحصل مكوّنات الأصناف على ref كـ prop في React 19؟

لا. المرجع على مكوّن صنف ما زال يشير إلى نسخة المكوّن. ينطبق التغيير على مكوّنات الدوال فقط.

رسم توضيحي للغات البرمجة في Coddy

تعلّم البرمجة مع Coddy

ابدأ الآن