forwardRef
forwardRef memungkinkan Anda mengekspos sebuah simpul DOM sebagai sebuah ref kepada induknya.
const SomeComponent = forwardRef(render)Referensi
forwardRef(render)
Panggil fungsi forwardRef() untuk membiarkan komponen Anda menerima ref dan meneruskannya ke komponen anak:
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
// ...
});[Lihat contoh lainnya di bawah ini.] (#usage)
Parameter
render: Fungsi render untuk komponen Anda. React memanggil fungsi ini dengan props danrefyang diterima komponen Anda dari induknya. JSX yang Anda kembalikan akan menjadi keluaran dari komponen Anda.
Kembalian
forwardRef mengembalikan komponen React yang dapat Anda render di JSX. Tidak seperti komponen React yang didefinisikan sebagai fungsi biasa, komponen yang dikembalikan oleh forwardRef juga dapat menerima prop ref.
Peringatan
- Dalam Mode Ketat, React akan memanggil fungsi render Anda dua kali untuk membantu Anda menemukan ketidakmurnian yang tidak disengaja. Ini adalah perilaku khusus pengembangan dan tidak mempengaruhi produksi. Jika fungsi render Anda murni (sebagaimana mestinya), hal ini tidak akan mempengaruhi logika komponen Anda. Hasil dari salah satu pemanggilan akan diabaikan.
render function
forwardRef menerima fungsi render sebagai argumen. React memanggil fungsi ini dengan props dan ref:
const MyInput = forwardRef(function MyInput(props, ref) {
return (
<label>
{props.label}
<input ref={ref} />
</label>
);
});Parameter
-
props: props yang dioperkan oleh komponen induk. -
ref: Atributrefyang dioper oleh komponen induk.refdapat berupa objek atau fungsi. Jika komponen induk tidak mengoper ref, maka akan menjadinull. Anda harus mengoperrefyang Anda terima ke komponen lain, atau mengopernya keuseImperativeHandle.
Kembalian
forwardRef mengembalikan komponen React yang dapat Anda render di JSX. Tidak seperti komponen React yang didefinisikan sebagai fungsi biasa, komponen yang dikembalikan oleh forwardRef dapat mengambil sebuah prop ref.
Penggunaan
Mengekspos sebuah simpul DOM ke komponen induk
Secara default, simpul-simpul DOM dari setiap komponen bersifat privat. Namun, terkadang berguna untuk mengekspos simpul DOM ke induknya - misalnya, untuk memungkinkan pemfokusan. Untuk ikut serta, bungkus definisi komponen Anda ke dalam forwardRef():
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} />
</label>
);
});Anda akan menerima ref sebagai argumen kedua setelah props. Berikan ke simpul DOM yang ingin Anda ekspos:
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} ref={ref} />
</label>
);
});Hal ini memungkinkan komponen Form induk mengakses <input> DOM node yang diekspos oleh MyInput:
function Form() {
const ref = useRef(null);
function handleClick() {
ref.current.focus();
}
return (
<form>
<MyInput label="Enter your name:" ref={ref} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}Komponen MyInput meneruskan ref tersebut ke tag peramban <input>. Hasilnya, komponen Form dapat mengakses simpul DOM <input> tersebut dan memanggil fungsi focus() di atasnya.
Perlu diingat bahwa mengekspos ref ke simpul DOM di dalam komponen Anda akan mempersulit untuk mengubah internal komponen Anda di kemudian hari. Anda biasanya akan mengekspos simpul DOM dari komponen tingkat rendah yang dapat digunakan kembali seperti tombol atau input teks, tetapi Anda tidak akan melakukannya untuk komponen tingkat aplikasi seperti avatar atau komentar.
Contoh 1 dari 2: Memfokuskan input teks
Mengeklik tombol akan memfokuskan input. Komponen Form mendefinisikan sebuah ref dan meneruskannya ke komponen MyInput. Komponen MyInput meneruskan ref tersebut ke tag peramban <input>. Hal ini memungkinkan komponen Form memfokuskan <input>.
import { useRef } from 'react'; import MyInput from './MyInput.js'; export default function Form() { const ref = useRef(null); function handleClick() { ref.current.focus(); } return ( <form> <MyInput label="Enter your name:" ref={ref} /> <button type="button" onClick={handleClick}> Edit </button> </form> ); }
Meneruskan ref melalui beberapa komponen
Alih-alih meneruskan ref ke DOM node, Anda bisa meneruskannya ke komponen Anda sendiri seperti MyInput:
const FormField = forwardRef(function FormField(props, ref) {
// ...
return (
<>
<MyInput ref={ref} />
...
</>
);
});Jika komponen MyInput meneruskan sebuah ref ke <input>, sebuah ref ke FormField akan memberi Anda <input> tersebut:
function Form() {
const ref = useRef(null);
function handleClick() {
ref.current.focus();
}
return (
<form>
<FormField label="Enter your name:" ref={ref} isRequired={true} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}Komponen Form mendefinisikan sebuah ref dan meneruskannya ke FormField. Komponen FormField meneruskan ref tersebut ke MyInput, yang meneruskannya ke DOM node <input> peramban. Beginilah cara Form mengakses DOM node tersebut.
import { useRef } from 'react'; import FormField from './FormField.js'; export default function Form() { const ref = useRef(null); function handleClick() { ref.current.focus(); } return ( <form> <FormField label="Enter your name:" ref={ref} isRequired={true} /> <button type="button" onClick={handleClick}> Edit </button> </form> ); }
Mengekspos penanganan imperatif alih-alih sebuah simpul DOM
Daripada mengekspos seluruh simpul DOM, Anda dapat mengekspos objek khusus, yang disebut penanganan imperatif (imperative handle), dengan sekumpulan methods yang lebih terbatas. Untuk melakukan ini, Anda harus mendefinisikan ref terpisah untuk menampung DOM node:
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
// ...
return <input {...props} ref={inputRef} />;
});Berikan ref yang Anda terima ke useImperativeHandle dan tentukan nilai yang ingin Anda ekspos ke ref:
import { forwardRef, useRef, useImperativeHandle } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);
return <input {...props} ref={inputRef} />;
});Jika beberapa komponen mendapatkan referensi ke MyInput, komponen tersebut hanya akan menerima objek { focus, scrollIntoView }, bukan simpul DOM. Hal ini memungkinkan Anda membatasi informasi yang Anda paparkan tentang simpul DOM seminimal mungkin.
import { useRef } from 'react'; import MyInput from './MyInput.js'; export default function Form() { const ref = useRef(null); function handleClick() { ref.current.focus(); // This won't work because the DOM node isn't exposed: // ref.current.style.opacity = 0.5; } return ( <form> <MyInput placeholder="Enter your name" ref={ref} /> <button type="button" onClick={handleClick}> Edit </button> </form> ); }
Baca lebih lanjut tentang menggunakan penanganan imperatif.
Pemecahan Masalah
Komponen saya dibungkus dengan forwardRef, tetapi ref ke komponen tersebut selalu null
Hal ini biasanya berarti bahwa Anda lupa menggunakan ref yang Anda terima.
Sebagai contoh, komponen ini tidak melakukan apa pun dengan ref-nya:
const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input />
</label>
);
});Untuk memperbaikinya, berikan ref ke simpul DOM atau komponen lain yang dapat menerima ref:
const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input ref={ref} />
</label>
);
});ref ke Komponen MyInput juga dapat menjadi null jika beberapa logika bersyarat:
const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
{showInput && <input ref={ref} />}
</label>
);
});Jika showInput bernilai false, maka ref tidak akan diteruskan ke simpul mana pun, dan ref ke MyInput akan tetap kosong. Hal ini sangat mudah terlewatkan jika kondisi tersebut tersembunyi di dalam komponen lain, seperti Panel pada contoh ini:
const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
<Panel isExpanded={showInput}>
<input ref={ref} />
</Panel>
</label>
);
});