Menu

Hook useRef di React: Ref DOM dan Nilai yang Bertahan

useRef memberi komponen sebuah kotak, { current }, yang mempertahankan isinya di antara render tanpa memicu render saat kamu mengubahnya. Pakai untuk menjangkau node DOM (fokus, scroll, ukur) atau untuk menyimpan id timer atau nilai apa pun yang tidak ditampilkan di layar.

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

useRef mengembalikan sebuah object, { current: initialValue }, yang disimpan React selama komponen itu hidup. Mengubah ref.current tidak merender ulang apa pun. Itu membuat ref tepat untuk dua tugas: menyimpan node DOM agar kamu bisa memanggil method seperti focus() padanya, dan mengingat nilai di antara render yang tidak perlu ditampilkan di layar.

Memberikan object ref ke ref={inputRef} memberi tahu React untuk menaruh node DOM <input> di inputRef.current. Ganti focus() menjadi select(), ketik sesuatu, lalu klik lagi: teksnya akan tersorot.

Sintaksnya

const myRef = useRef(initialValue);

myRef.current;          // read
myRef.current = value;  // write, no re-render
  • initialValue hanya dipakai di render pertama. Untuk ref DOM, nilainya null.
  • React mengembalikan object yang sama di setiap render. Hanya current yang berubah.
  • Membaca dan menulis current adalah JavaScript biasa. React tidak melacaknya.

Nilai yang tidak memicu render

State dipakai untuk apa yang ditampilkan di layar. Ref dipakai untuk apa yang perlu diingat komponen tapi tidak ditampilkan. Blok ini menyimpan satu counter di ref dan satu di state, lalu mencatat keduanya:

Klik Ref + 1 beberapa kali. Console menunjukkan ref.current terus naik, tapi halaman masih menampilkan 0 dan tidak ada render yang tercatat. Sekarang klik State + 1: komponen dirender, dan counter ref di layar melompat ke angka yang sudah disimpan ref itu. Ref mempertahankan nilainya sepanjang waktu; React hanya tidak punya alasan untuk menggambarnya.

Ref vs state

useStateuseRef
Mengembalikan[value, setValue]{ current: value }
Saat diubahMerender komponen lagiTidak ada yang terlihat
Kapan nilai baru bisa dibacaDi render berikutnyaSeketika
Diubah denganSetterAssignment, ref.current = x
Dipakai untukApa pun yang ditampilkan di layarId timer, node DOM, nilai sebelumnya, flag

Kalau ragu, tanyakan apakah JSX membaca nilai itu. Kalau ya, nilai itu tempatnya di state.

Menyimpan id timer

setInterval mengembalikan id yang nanti kamu butuhkan untuk menghentikan timer. Id ini cocok disimpan di ref: handler Stop membutuhkannya, tapi layar tidak pernah menampilkannya, dan menyimpannya di variabel biasa akan membuatnya hilang di render berikutnya.

Ganti useRef(null) dengan let intervalRef = { current: null }; lalu coba Start, tunggu dua detik, kemudian Stop: timer-nya terus berjalan. Setiap detik komponen dirender, yang membuat object baru, jadi Stop tidak menghentikan apa pun.

Mengingat nilai sebelumnya

Effect berjalan setelah render, jadi effect bisa membandingkan nilai baru dengan nilai yang tersimpan di ref, lalu menyimpan nilai baru itu untuk berikutnya:

Klik green, lalu blue: console mencatat changed from red to green dan changed from green to blue. Mengklik warna yang sudah terpilih tidak mencatat apa pun, karena effect hanya berjalan saat color berubah.

Bekerja dengan DOM

Ref adalah cara memanggil API browser yang tidak punya prop di React: focus(), select(), scrollIntoView(), play() pada video, atau membaca ukuran dengan getBoundingClientRect().

const lastItemRef = useRef(null);

function scrollToLast() {
    lastItemRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}

<li ref={lastItemRef}>Last item</li>

ref.current bernilai null selama render pertama, karena React belum membuat node DOM-nya, dan begitu pula setelah elemennya dihapus. Baca di event handler dan effect, tempat node itu sudah ada. Untuk mengukur elemen dan memosisikan sesuatu sebelum browser menggambar layar, baca ref di useLayoutEffect.

Jangan membaca atau menulis ref.current saat render

React mengharapkan badan komponen berupa perhitungan murni: props dan state yang sama, JSX yang sama. Ref berubah di luar model itu, jadi render yang membaca atau menulisnya bisa menampilkan hasil berbeda untuk input yang sama, dan React mungkin memanggil badan komponen lebih sering dari yang kamu kira.

function Bad() {
    const count = useRef(0);
    count.current++;              // writing during render
    return <p>{count.current}</p>; // reading during render
}

Baca dan tulis ref di event handler dan effect. Satu-satunya pengecualian adalah inisialisasi lazy, saat kamu mengisi ref sekali di render pertama:

const playerRef = useRef(null);
if (playerRef.current === null) {
    playerRef.current = new VideoPlayer(); // runs once
}

(Demo counter ref di atas sengaja membaca clicks.current di JSX, untuk menunjukkan bahwa layar tidak mengikutinya. Jangan menyalin cara itu ke kode sungguhan.)

Ref callback

Alih-alih object ref, kamu bisa memberikan sebuah fungsi ke ref. React memanggilnya dengan node DOM saat elemen itu ditambahkan. Di React 19, fungsi itu bisa mengembalikan cleanup, yang dipanggil React saat elemennya dihapus. Ini berguna untuk sebuah daftar, karena kamu tidak bisa memanggil useRef sekali per item:

Saat dimuat, console mencatat satu baris attached per item. Klik Remove last dan hanya detached Cheese yang muncul. trackNode didefinisikan di luar komponen, jadi fungsinya sama di setiap render dan React membiarkan item lainnya. Tulis secara inline sebagai ref={(node) => { ... }} dan setiap klik akan melepas lalu memasang ulang setiap item, karena setiap render memberikan fungsi baru.

Memberikan ref ke komponenmu sendiri

Di React 19, ref adalah prop biasa untuk function component. Parent membuat ref-nya, dan child memasangnya di elemen DOM yang ingin dibukanya:

Sebelum React 19, ini membutuhkan forwardRef, yang masih berfungsi. Halaman forwardRef menunjukkan kedua versi dan cara membuka hanya beberapa method dengan useImperativeHandle.

Kesalahan umum

Menampilkan nilai ref di JSX. Layar tidak mengikutinya. Kalau pengguna perlu melihat nilainya, pakai state.

Memakai ref.current sebagai dependensi effect. [ref.current] membaca nilainya saat render dan React tidak tahu kapan nilai itu berubah, jadi effect tidak berjalan ulang saat nilainya berubah. Pakai ref callback untuk bereaksi saat sebuah node dipasang.

Membaca ref DOM selama render pertama. Nilainya null sampai React membuat node-nya. Baca di effect atau handler.

Membuat object baru alih-alih memakai hook. const ref = { current: null } di badan komponen adalah object baru di setiap render. Hanya useRef yang memberimu object yang sama setiap kali.

Pertanyaan yang sering diajukan

Apa yang dikembalikan useRef?

Object biasa dengan satu property, current, yang diatur ke nilai awal yang kamu berikan. React mengembalikan object yang sama di setiap render, jadi apa pun yang kamu taruh di current masih ada di render berikutnya.

Apa bedanya useRef dan useState?

Mengubah state merender komponen lagi; mengubah ref.current tidak. Pakai state untuk nilai yang ditampilkan di layar, dan ref untuk nilai yang hanya dibutuhkan event handler dan effect-mu, seperti id timer atau node DOM.

Mengapa ref.current bernilai null?

React mengatur ref.current ke node DOM setelah membuat node itu, saat commit. Selama render pertama nilainya masih null, dan kembali menjadi null setelah elemennya dihapus. Baca di event handler atau effect.

Apakah mengubah ref.current memicu render ulang?

Tidak. React tidak tahu bahwa kamu mengubahnya. Nilai baru baru muncul di layar kalau ada hal lain, seperti pembaruan state, yang merender komponen itu lagi.

Bagaimana cara memberikan ref ke komponen child?

Di React 19, function component menerima ref sebagai prop biasa, jadi kamu memberikan <MyInput ref={inputRef} /> dan child memasangnya di <input> miliknya. Versi yang lebih lama membutuhkan forwardRef.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai