يُرجع useId معرّفًا فريدًا لكل نسخة من المكوّن، والمعرّف نفسه في كل عرض. استخدمه لربط <label> بحقله <input>، أو حقل بتلميحه عبر aria-describedby، عندما يمكن أن يظهر المكوّن أكثر من مرة في الصفحة.
يُعرض المكوّن نفسه مرتين ويحصل على معرّفين مختلفين، مطبوعين تحت كل حقل. انقر النص "Confirm password" فينتقل التركيز إلى الحقل الثاني، لأن htmlFor الخاصة به لا تطابق إلا id ذلك الحقل. اكتب id="password" ثابتًا مكان useId() فتشير التسميتان كلتاهما إلى الحقل الأول.
الصيغة
const id = useId();
لا يأخذ useId وسائط ويُرجع نصًا. استدعِه في المستوى الأعلى من المكوّن، مثل كل خطاف. صيغته الدقيقة داخلية وتغيّرت بين الإصدارات: كانت React 18 تنتج :r1:، وتنتج React 19.2 القيمة _r_1_ لمكوّن عُرض أولًا في المتصفح، ومعرّفًا يبدأ بـ _R_ مبنيًا من موضع الشجرة لمكوّن عُرض على الخادم. لا تحلّله أبدًا ولا تعتمد على شكله.
لماذا لا Math.random ولا عدّاد
يجب أن تتطابق معرّفات إمكانية الوصول بين HTML الذي يرسله الخادم والشجرة التي تبنيها React في المتصفح. والطريقتان البديهيتان لصنع معرّف تفشلان كلتاهما في هذا الاختبار.
// Changes on every render, and differs between server and browser
const id = 'field-' + Math.random().toString(36).slice(2);
// The server's counter keeps growing across requests, the browser starts at 0
let nextId = 0;
const id = 'field-' + nextId++;
مع العرض على الخادم (Next.js، أو وضع إطار العمل في React Router، أو أي إعداد يستخدم hydrateRoot)، يطبع الخادم id="field-4817" في HTML، ويحسب أول عرض في المتصفح field-0، وتبلّغ React عن عدم تطابق في الإماهة. أما useId فيبني المعرّف من موضع المكوّن في الشجرة، وهو مطابق في الجانبين.
وحتى دون خادم، قد يتغير المعرّف المبني أثناء العرض في كل عرض. يُظهر هذا المثال الفرق دون أي خادم:
انقر الزر بضع مرات. تبقى قيمة useId ثابتة بينما يتصاعد معرّف العدّاد مع كل عرض، فكل ما كان يشير إلى المعرّف القديم (aria-describedby أو تسمية) يشير الآن إلى لا شيء. تغليف العدّاد في useState(() => nextId++) سيصلح إعادة العرض لكن لا يصلح عدم التطابق مع الخادم.
عدة معرّفات من استدعاء واحد
المكوّن الذي فيه عدة حقول لا يحتاج إلى عدة استدعاءات لـ useId. ولّد أساسًا واحدًا وأضف لاحقة لكل عنصر.
اكتب كلمة دون @ في حقل البريد الإلكتروني: يظهر الخطأ وتشير aria-describedby الخاصة بالحقل إليه، فيقرأ قارئ الشاشة الخطأ عندما يُركَّز على الحقل. اعرض <SignupForm /> مرتين في App فتحصل كل نسخة على معرّفها الأساسي الخاص.
ليس لمفاتيح القوائم
المفاتيح والمعرّفات تحل مشكلات مختلفة. يخبر المفتاح React أي عنصر هو أي عنصر بين مرات العرض، فيجب أن يأتي من البيانات. ويعطي useId معرّفًا واحدًا لكل نسخة مكوّن، ولا يمكنك استدعاؤه داخل map.
// Wrong: breaks the rules of hooks, and the key is unrelated to the item
{todos.map((todo) => <Todo key={useId()} todo={todo} />)}
// Right: the key comes from the data
{todos.map((todo) => <Todo key={todo.id} todo={todo} />)}
عندما لا يكون لبياناتك معرّف، أنشئ واحدًا عند إنشاء العنصر (crypto.randomUUID() في معالج الحدث الذي يضيفه)، لا أثناء العرض. تشرح صفحة القوائم والمفاتيح لماذا يجب أن يبقى المفتاح مع عنصره.
عدة جذور React في صفحة واحدة
إذا عُرض تطبيقا React منفصلان في الصفحة نفسها، فقد تتصادم معرّفاتهما. أعطِ كل جذر بادئة:
createRoot(document.getElementById('cart'), { identifierPrefix: 'cart-' });
createRoot(document.getElementById('chat'), { identifierPrefix: 'chat-' });
مع العرض على الخادم، مرّر identifierPrefix نفسها إلى مُصيِّر الخادم وإلى hydrateRoot لينتج الجانبان المعرّفات نفسها.
أخطاء شائعة
عرض شجرة مختلفة على الخادم وفي المتصفح. يعتمد useId على موضع المكوّن، لذا قد يزيح فرع مثل typeof window === 'undefined' ? <A /> : <B /> فوق حقل المعرّفات بين العرضين. أبقِ الشجرة نفسها أثناء الإماهة وبدّل بعدها، في تأثير.
البحث عن العنصر بمعرّفه. يعمل document.getElementById(id)، لكن المرجع هو طريقة React للوصول إلى عقدة DOM ولا يحتاج إلى معرّف أصلًا.
استخدامه كقيمة عشوائية. المعرّف فريد داخل التطبيق، لكنه ليس عشوائيًا، ويمكن توقعه من الشجرة. لا تستخدمه لرموز الأمان أو مفاتيح الذاكرة المؤقتة أو أي شيء يُخزَّن عبر الجلسات.
متى تستخدمه
استخدم useId كلما احتاج مكوّن قابل لإعادة الاستخدام إلى سمة id: حقول نماذج مبنية من مكوّن يُستخدم مرات عديدة، أو تلميح مرتبط بـ aria-describedby، أو نافذة حوار بـ aria-labelledby، أو تبويبات بـ aria-controls. وعندما لا يربط المعرّف إلا تسمية بحقلها، يمكنك أيضًا الاستغناء عن المعرّف ووضع الحقل داخل التسمية (<label>Name <input /></label>)؛ واستخدم useId عندما لا يمكن تداخل العناصر.
الأسئلة الشائعة
ماذا يفعل useId في React؟
يُرجع نصًا فريدًا لنسخة المكوّن تلك ويبقى نفسه في كل عرض. تستخدمه لربط العناصر بالمعرّف: htmlFor على تسمية، وaria-describedby على حقل، وaria-labelledby على نافذة حوار.
لماذا لا أستخدم Math.random() أو عدّادًا للمعرّفات؟
يعطيان قيمًا مختلفة على الخادم وفي المتصفح، فتختلف الصفحة المعروضة على الخادم عن نسختها بعد الإماهة وتبلّغ React عن عدم تطابق في الإماهة. ويتغير Math.random() أيضًا في كل عرض. أما useId فيشتق المعرّف من موضع المكوّن في الشجرة، وهو نفسه في المكانين.
هل يمكنني استخدام useId لمفاتيح قائمة؟
لا. يجب أن يأتي المفتاح من بياناتك لتتمكن React من مطابقة العنصر نفسه بين مرات العرض. يُستدعى useId مرة لكل مكوّن، واستدعاؤه داخل map يخرق قواعد الخطافات على أي حال. استخدم معرّف العنصر نفسه.
كيف أحصل على عدة معرّفات من استدعاء واحد لـ useId؟
استدعِ useId مرة واحدة وأضف لواحق: ${id}-name و${id}-email. الأساس فريد، فتكون النصوص ذات اللواحق فريدة أيضًا.
هل يمكنني استخدام المعرّف من useId في محدد CSS؟
تجنّب ذلك. المعرّف مخصص لربط العناصر في DOM، وصيغته الدقيقة تفصيل داخلي تغيّر بين إصدارات React. نسّق بصنف، وابحث عن العناصر بمرجع بدلًا من querySelector.