useId هو خطاف react يستخدم لإنشاء معرفات فريدة يمكن تمريرها إلى سمات إمكانية الوصول.

const id = useId()

المرجع

useId()

استدع useId في المستوى الأعلى لمكونك لإنشاء معرف فريد

import { useId } from 'react';

function PasswordField() {
const passwordHintId = useId();
// ...

يرجى الإطلاع على المزيد من الأمثلة بالأسفل.

المعاملات (parameters)

useId لا يقبل أي معاملات.

العائدات

useId يعيد نص فريد مرتبط باستدعاء useId المستخدم في هذا المكون تحديدا.

تنبيهات

  • useId هو خطاف، لذلك يمكنك استدعائه فقط في المستوي الأعلي من مكونك أو من خلال الخطاطيف الخاصة بك. لا يمكنك استدعاء الخطاف داخل الحلقات والشروط. إذا كنت بحاجة إلي ذلك، قم بإستخراج مكون جديد وقم بنقل الحالة إليه.

  • useId لا ينبغي استخدامه لتوليد المفاتيح في القائمة. يجب أن تتم إنشاء المفاتيح من البيانات الخاصة بك.


الاستخدام

Pitfall

لا تستدعِ useId لتوليد المفاتيح في القائمة. يجب أن تتم إنشاء المفاتيح من البيانات الخاصة بك.

إنشاء معرفات فريدة لسمات إمكانية الوصول

استدعِ useId في المستوي الأعلي من المكون الخاص بك لإنشاء معرف فريد:

import { useId } from 'react';

function PasswordField() {
const passwordHintId = useId();
// ...

يمكنك بعد ذلك تمرير المعرف الذى تم إنشاؤه إلى سمات مختلفة:

<>
<input type="password" aria-describedby={passwordHintId} />
<p id={passwordHintId}>
</>

دعنا نستعرض مثالا لمعرفة متى يكون ذلك مفيدا.

HTML accessibility attributes مثل aria-describedby تتيح لك تحديد أن هناك علامتين مرتبطين ببعضهما البعض. على سبيل المثال، يمكنك تحديد أن العنصر (مثل صندوق الإدخال) يتم وصفه بواسطة عنصر آخر (مثل فقرة).

في HTML العادي ستكتبه بهذا الشكل:

<label>
Password:
<input
type="password"
aria-describedby="password-hint"
/>
</label>
<p id="password-hint">
يجب أن تحتوي كلمة السر على 18 حرفًا على الأقل
</p>

مع ذلك، تضمين المعرفات بهذا الشكل ليس طريقة جيدة في React. يمكن أن يتم عرض المكون أكثر من مرة على الصفحة، ولكن يجب أن تكون المعرفات فريدة! بدلا من تضمين معرف ثابت، يمكنك توليد معرف فريد باستخدام useId:

import { useId } from 'react';

function PasswordField() {
const passwordHintId = useId();
return (
<>
<label>
Password:
<input
type="password"
aria-describedby={passwordHintId}
/>
</label>
<p id={passwordHintId}>
يجب أن تحتوي كلمة السر على 18 حرفًا على الأقل
</p>
</>
);
}

الآن، حتى إذا كان PasswordField يظهر عدة مرات على الشاشة، لن تحدث تعارضات بين المعرفات المولدة.

import { useId } from 'react';

function PasswordField() {
  const passwordHintId = useId();
  return (
    <>
      <label>
        Password:
        <input
          type="password"
          aria-describedby={passwordHintId}
        />
      </label>
      <p id={passwordHintId}>
        يجب أن تحتوي كلمة السر على 18 حرفًا على الأقل
      </p>
    </>
  );
}

export default function App() {
  return (
    <>
      <h2>أدخل كلمة سر</h2>
      <PasswordField />
      <h2>تأكيد كلمة السر</h2>
      <PasswordField />
    </>
  );
}

شاهد هذا الفيديو لترى الفرق في تجربة المستخدم مع تقنيات المساعدة.

Pitfall

في تصيير الخادم, يتطلب useId وجود نفس شجرة المكونات علي الخادم والعميل. إذا لم تتطابق الشجرات التي تقوم بعرضها على الخادم والعميل حرفيا، فإن المعرفات المولدة لن تتطابق.

Deep Dive

لماذا يعد useId أفضل من العداد المتزايد؟

قد تتساءل لماذا useId أفضل من زيادة متغير عالمي مثل nextId++.

الفائدة الأساسية لـ useId هي أن React ستضمن أنه يعمل مع تصيير الخادم. أثناء تصيير الخادم، يتم تحويل مكوناتك إلي عناصر HTML. في وقت لاحق، على العميل، hydration يقوم بربط معالجات الأحداث الخاصة بك بعناصر HTML التي تم توليدها. لكي يعمل تحويل العناصر على العميل بشكل صحيح، يجب أن يتطابق إخراج العميل مع HTML الذي على الخادم.

من الصعب جدا ضمان ذلك باستخدام عداد متزايد لأن ترتيب تحويل المكونات على العميل قد لا يتطابق مع ترتيب إخراج HTML على الخادم. من خلال استدعاء useId، ستضمن أن عملية تحويل المكونات ستعمل بشكل صحيح، وسيتطابق الإخراج بين الخادم والعميل.

داخل React، يتم إنشاء useId من الـ “مسار الأب” للمكون الذي يستدعيه. وهذا هو السبب في أنه إذا كانت شجرة العميل وشجرة الخادم متطابقتين، سيتطابق “مسار لأب” بغض النظر عن ترتيب العرض.


إذا كنت بحاجة إلى تعيين معرفات لعدة عناصر ذات صلة، يمكنك استدعاء useId لتوليد بادئة مشتركة لها:

import { useId } from 'react';

export default function Form() {
  const id = useId();
  return (
    <form>
      <label htmlFor={id + '-firstName'}>الاسم الأول:</label>
      <input id={id + '-firstName'} type="text" />
      <hr />
      <label htmlFor={id + '-lastName'}>الاسم الأخير:</label>
      <input id={id + '-lastName'} type="text" />
    </form>
  );
}

هذا يتيح لك تجنب استدعاء useId لكل عنصر يحتاج إلى معرف فريد.


تحديد بادئة مشتركة لجميع المعرفات المولدة

إذا كنت تقوم بعرض عدة تطبيقات react مستقلة على صفحة واحدة, قم بتمرير identifierPrefix كخيار إلى استدعاءات createRoot أو hydrateRoot الخاصة بك. هذا يضمن عدم حدوث تعارض بين المعرفات المولدة بواسطة التطبيقين المختلفين لأن كل معرف تم إنشاؤه باستخدام useId سيبدأ بالبادئة المميزة التي حددتها.

import { createRoot } from 'react-dom/client';
import App from './App.js';
import './styles.css';

const root1 createRoot(document.getElementById('root1'), {
  identifierPrefix: 'my-first-app-'
});
root1.render(<App />);

const root2 = createRoot(document.getElementById('root2'), {
  identifierPrefix: 'my-second-app-'
});
root2.render(<App />);


Using the same ID prefix on the client and the server

If you render multiple independent React apps on the same page, and some of these apps are server-rendered, make sure that the identifierPrefix you pass to the hydrateRoot call on the client side is the same as the identifierPrefix you pass to the server APIs such as renderToPipeableStream.

// Server
import { renderToPipeableStream } from 'react-dom/server';

const { pipe } = renderToPipeableStream(
<App />,
{ identifierPrefix: 'react-app1' }
);
// Client
import { hydrateRoot } from 'react-dom/client';

const domNode = document.getElementById('root');
const root = hydrateRoot(
domNode,
reactNode,
{ identifierPrefix: 'react-app1' }
);

You do not need to pass identifierPrefix if you only have one React app on the page.