Menu

forwardRef React: Mengirim Ref, dan Ref sebagai Prop di 19

forwardRef memungkinkan sebuah komponen menerima ref dari induknya dan memasangnya ke node DOM di dalamnya. Di React 19, function component menerima ref sebagai prop biasa, jadi kode baru tidak lagi butuh forwardRef. Lihat kedua versi berjalan, ditambah useImperativeHandle dan tipe TypeScript.

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

forwardRef memungkinkan komponen induk mengirim ref melewati komponenmu ke elemen DOM di dalamnya, sehingga induk bisa memanggil focus(), scrollIntoView(), dan method serupa pada elemen itu. Di React 19, kamu tidak lagi membutuhkannya: function component menerima ref sebagai prop biasa. forwardRef masih berjalan, dan kamu akan menemuinya di sebagian besar kode yang sudah ada, jadi halaman ini menunjukkan keduanya.

forwardRef menerima fungsi render dengan dua argumen: props, dan ref yang dikirim induk. ref tidak berada di dalam props; ia datang tersendiri. Klik Focus, dan konsol memastikan bahwa inputRef.current adalah node <input> yang sesungguhnya.

Cara React 19: ref adalah prop

Sejak React 19, function component mendapat ref di props-nya seperti prop lain. Tanpa wrapper, tanpa argumen kedua:

Ketik sesuatu lalu klik Log value: induknya membaca teks terkini input langsung dari node DOM. Hapus ref={ref} dari <input> lalu klik Focus: konsol menampilkan error, karena inputRef.current sekarang bernilai null.

forwardRef (semua versi)Prop ref (React 19+)
Cara komponen mendapat refArgumen kedua, (props, ref)props.ref
Butuh wrapperYaTidak
Bisa dipakai dengan useImperativeHandleYaYa
StatusBerjalan, direncanakan deprecatedCara menulis kode baru

Ref yang diabaikan anak tetap null

Mengirim ref ke sebuah komponen tidak melakukan apa pun dengan sendirinya. Komponen itu harus memasangnya ke sebuah elemen. Jika tidak, ref.current tetap null:

Konsol menampilkan Forgetful ref: null dan Careful ref: INPUT. Inilah alasan paling umum di balik "ref.current is null" pada komponen buatan sendiri. Sebelum React 19, Careful pun akan bernilai null kecuali dibungkus forwardRef, karena ref tidak pernah diserahkan ke fungsinya.

useImperativeHandle: tampilkan API yang lebih kecil

Memberi induk seluruh node DOM berarti induk bisa melakukan apa saja padanya: mengubah gaya, menghapus anak, membaca nilai yang ingin kamu rahasiakan. useImperativeHandle memungkinkanmu menentukan isi ref.current sebagai gantinya:

Tombol terakhir mencatat ["focus", "clear"]: induk mendapat dua method itu dan tidak ada yang lain, bukan node input-nya. Tambahkan method select() ke objek itu dan sebuah tombol yang memanggilnya, dan induk mendapat tepat satu kemampuan tambahan.

Komponen anak menyimpan ref-nya sendiri, inputRef, untuk input yang sesungguhnya, dan handle membungkusnya. Argumen ketiga adalah array dependensi, seperti milik useEffect: handle dibuat ulang saat nilai-nilai itu berubah.

Pakai ini secukupnya. Sebagian besar yang diinginkan induk (membuka, menutup, menampilkan error) lebih baik dijadikan props, seperti isOpen atau error. Method imperatif cocok untuk aksi yang tidak punya prop alami: memberi fokus, scroll, memutar video, memulai animasi.

Memakai ref di dalam anak juga

Kadang komponen anak butuh node DOM yang sama untuk keperluannya sendiri, misalnya untuk mengukurnya atau memberinya fokus setelah error, sementara induk juga memegang ref ke node itu. Satu atribut ref hanya bisa menerima satu nilai, jadi gabungkan keduanya dengan ref callback:

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 milik induk bisa berupa objek dari useRef atau sebuah fungsi, jadi tangani keduanya. Saat induk hanya butuh beberapa aksi, useImperativeHandle di atas adalah pilihan yang lebih rapi, karena komponen anak menyimpan node itu untuk dirinya sendiri.

Mengirim ref melewati beberapa lapisan

Ref berpindah satu komponen demi satu komponen. Jika Form merender Field, yang merender TextInput, yang merender <input>, setiap komponen itu harus meneruskan ref ke komponen berikutnya. Di React 19, itu hanya satu prop tambahan untuk diteruskan (<TextInput ref={ref} />); dengan forwardRef, setiap lapisan harus dibungkus. Spread props ({...props}) tidak membawanya di versi lama, karena sebelum React 19 ref tidak pernah menjadi bagian dari props.

Komponen design system adalah tempat yang biasanya membuat hal ini penting: Button, Input, atau Select yang membungkus elemen bawaan sebaiknya meneruskan ref-nya, agar aplikasi yang memakainya bisa memberinya fokus, mengukurnya, atau menyerahkannya ke library yang mengatur posisi popover.

Migrasi dari forwardRef

Perubahannya mekanis: hapus wrapper-nya dan baca ref dari 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} />;
}

Tidak perlu terburu-buru. forwardRef tetap berjalan di React 19, dan library yang harus mendukung React 18 perlu mempertahankannya, karena React 18 tidak mengirim ref sebagai prop. Class component tidak terpengaruh: ref pada class component tetap menunjuk ke instance komponen.

TypeScript

Dengan forwardRef, argumen tipenya datang dengan urutan tipe ref, lalu 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} />;
    }
);

Di React 19, beri tipe pada ref seperti prop lain. ComponentProps<'input'> sudah mencakupnya:

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);

Halaman useRef membahas ref itu sendiri: akses DOM, nilai yang bertahan tanpa memicu render, dan ref callback. Untuk memberi tipe pada komponen secara umum, lihat React dengan TypeScript.

Pertanyaan yang sering diajukan

Apa fungsi forwardRef di React?

forwardRef membungkus function component agar ref yang diberikan induk sampai ke komponen itu sebagai argumen kedua, (props, ref). Komponen itu lalu memasang ref tersebut ke sebuah node DOM, sehingga induknya bisa memanggil method seperti focus() pada node itu.

Apakah forwardRef sudah deprecated di React 19?

Belum, dan masih berjalan. React 19 mengirim ref ke function component sebagai prop biasa, jadi kode baru tidak membutuhkannya, dan tim React sudah menyatakan rencana untuk menjadikan forwardRef deprecated di versi mendatang.

Kenapa ref saya bernilai null pada komponen buatan sendiri?

Komponen itu menerima ref, tetapi tidak memasangnya ke elemen mana pun. Pasang ke node DOM di dalamnya: <input ref={ref} />. Sebelum React 19, ref sama sekali tidak dikirim kecuali komponennya dibungkus forwardRef.

Untuk apa useImperativeHandle dipakai?

useImperativeHandle menggantikan apa yang dilihat induk di ref.current. Alih-alih seluruh node DOM, kamu mengembalikan objek yang hanya berisi method pilihanmu, seperti focus dan clear.

Apakah class component menerima ref sebagai prop di React 19?

Tidak. Ref pada class component tetap menunjuk ke instance komponen. Perubahan ini hanya berlaku untuk function component.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai