Menu

Hook useId di React: ID Unik untuk Label dan ARIA

useId mengembalikan id yang unik dan stabil untuk setiap instance komponen, sehingga label, input, dan petunjuk aria-describedby bisa saling menunjuk meskipun komponennya muncul berkali-kali. Pelajari mengapa Math.random dan counter rusak dengan server rendering, dan mengapa useId bukan untuk key daftar.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

useId mengembalikan id unik untuk setiap instance sebuah komponen, dan id yang sama di setiap render. Pakai untuk menghubungkan <label> dengan <input>-nya, atau input dengan petunjuknya lewat aria-describedby, saat komponen itu bisa muncul lebih dari sekali di satu halaman.

Komponen yang sama dirender dua kali dan mendapat dua id berbeda, yang dicetak di bawah setiap field. Klik teks "Confirm password" dan fokus melompat ke input kedua, karena htmlFor miliknya hanya cocok dengan id input itu. Tulis langsung id="password" sebagai pengganti useId() dan kedua label akan menunjuk ke input pertama.

Sintaksnya

const id = useId();

useId tidak menerima argumen dan mengembalikan string. Panggil di tingkat teratas komponen, seperti setiap hook. Format persisnya bersifat internal dan sudah berubah di antara versi: React 18 menghasilkan :r1:, dan React 19.2 menghasilkan _r_1_ untuk komponen yang pertama kali dirender di browser, serta id yang diawali _R_, yang disusun dari posisi di pohon, untuk komponen yang dirender di server. Jangan pernah mem-parsing-nya atau bergantung pada bentuknya.

Mengapa tidak memakai Math.random atau counter

Id untuk aksesibilitas harus cocok antara HTML yang dikirim server dan pohon yang dibangun React di browser. Dua cara yang paling terpikir untuk membuat id sama-sama gagal dalam ujian itu.

// 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++;

Dengan server rendering (Next.js, mode framework React Router, setup hydrateRoot apa pun), server mencetak id="field-4817" ke HTML, render pertama di browser menghitung field-0, dan React melaporkan hydration mismatch. useId menyusun id dari posisi komponen di pohon, yang identik di kedua sisi.

Bahkan tanpa server, id yang dibuat saat render bisa berubah di setiap render. Contoh ini menunjukkan perbedaannya tanpa melibatkan server:

Klik tombolnya beberapa kali. Nilai useId tetap di tempatnya sementara id dari counter terus naik di setiap render, jadi apa pun yang menunjuk ke id lama (sebuah aria-describedby, sebuah label) sekarang tidak menunjuk ke mana pun. Membungkus counter itu dengan useState(() => nextId++) akan memperbaiki masalah render ulang, tapi tidak masalah ketidakcocokan dengan server.

Beberapa id dari satu pemanggilan

Komponen dengan beberapa field tidak butuh beberapa pemanggilan useId. Buat satu dasar lalu tambahkan akhiran untuk setiap elemen.

Ketik sebuah kata tanpa @ di field email: error-nya muncul dan aria-describedby milik input menunjuk ke sana, jadi screen reader membacakan error itu saat input difokuskan. Render <SignupForm /> dua kali di App dan setiap salinan mendapat id dasarnya sendiri.

Bukan untuk key daftar

Key dan id menyelesaikan masalah yang berbeda. Key memberi tahu React item mana yang mana di setiap render, jadi key harus berasal dari data. useId memberi satu id per instance komponen, dan kamu tidak bisa memanggilnya di dalam 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} />)}

Saat datamu tidak punya id, buat id saat item itu dibuat (crypto.randomUUID() di event handler yang menambahkannya), bukan saat render. Halaman daftar dan key menjelaskan mengapa key harus tetap melekat pada item-nya.

Beberapa root React di satu halaman

Kalau dua aplikasi React terpisah dirender di halaman yang sama, id-nya bisa bertabrakan. Beri setiap root sebuah awalan:

createRoot(document.getElementById('cart'), { identifierPrefix: 'cart-' });
createRoot(document.getElementById('chat'), { identifierPrefix: 'chat-' });

Dengan server rendering, berikan identifierPrefix yang sama ke renderer server dan ke hydrateRoot agar kedua sisi menghasilkan id yang sama.

Kesalahan umum

Merender pohon yang berbeda di server dan di browser. useId bergantung pada posisi komponen, jadi cabang seperti typeof window === 'undefined' ? <A /> : <B /> di atas sebuah field bisa menggeser id di antara kedua render. Jaga pohonnya tetap sama selama hidrasi dan ganti setelahnya, di dalam effect.

Mencari elemen berdasarkan id-nya. document.getElementById(id) memang berfungsi, tapi ref adalah cara React untuk menjangkau node DOM dan sama sekali tidak butuh id.

Memakainya sebagai nilai acak. Id itu unik di dalam aplikasi, bukan acak, dan bisa ditebak dari pohonnya. Jangan memakainya untuk token keamanan, kunci cache, atau apa pun yang disimpan lintas sesi.

Kapan memakainya

Pakai useId setiap kali komponen yang bisa dipakai ulang butuh atribut id: field formulir yang dibangun dari komponen yang dipakai berkali-kali, tooltip yang dihubungkan dengan aria-describedby, dialog dengan aria-labelledby, tab dengan aria-controls. Saat sebuah id hanya menghubungkan label dengan input-nya, kamu juga bisa melewatkan id dan menaruh input di dalam label (<label>Name <input /></label>); pakai useId saat elemen-elemennya tidak bisa disusun bersarang.

Pertanyaan yang sering diajukan

Apa fungsi useId di React?

useId mengembalikan string yang unik untuk instance komponen itu dan tetap sama di setiap render. Kamu memakainya untuk menghubungkan elemen lewat id: htmlFor pada label, aria-describedby pada input, aria-labelledby pada dialog.

Mengapa tidak memakai Math.random() atau counter untuk id?

Keduanya memberi nilai berbeda di server dan di browser, sehingga halaman yang dirender server dan versi hasil hidrasinya tidak sama, dan React melaporkan hydration mismatch. Math.random() juga berubah di setiap render. useId menurunkan id dari posisi komponen di pohon, yang sama di kedua tempat.

Bisakah useId dipakai untuk key di sebuah daftar?

Tidak. Key harus berasal dari datamu agar React bisa mencocokkan item yang sama di setiap render. useId dipanggil sekali per komponen, dan memanggilnya di dalam map pun melanggar aturan hook. Pakai id milik item itu sendiri.

Bagaimana cara mendapatkan beberapa id dari satu pemanggilan useId?

Panggil useId sekali lalu tambahkan akhiran: ${id}-name, ${id}-email. Dasarnya unik, jadi string yang diberi akhiran juga unik.

Bisakah id dari useId dipakai di selector CSS?

Hindari. Id itu dimaksudkan untuk menghubungkan elemen di DOM, dan format persisnya adalah detail internal yang sudah berubah di antara versi React. Beri style dengan class, dan cari elemen dengan ref alih-alih querySelector.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai