forwardRef
forwardRef
memungkinkan Anda mengekspos sebuah DOM node 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)
Parameters
render
: Fungsi render untuk komponen Anda. React memanggil fungsi ini dengan props danref
yang diterima komponen Anda dari induknya. JSX yang Anda kembalikan akan menjadi keluaran dari komponen Anda.
Mengembalikan
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>
);
});
Parameters
-
props
: props yang dioperkan oleh komponen induk. -
ref
: Atributref
yang dioper oleh komponen induk.ref
dapat berupa objek atau fungsi. Jika komponen induk tidak mengoper ref, maka akan menjadinull
. Anda harus mengoperref
yang Anda terima ke komponen lain, atau mengopernya keuseImperativeHandle
.
Returns
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 DOM node ke komponen induk
Secara default, setiap DOM nodes komponen bersifat privat. Namun, terkadang berguna untuk mengekspos DOM node 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 DOM node 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 DOM node <input>
tersebut dan memanggil fungsi focus()
di atasnya.
Perlu diingat bahwa mengekspos ref ke DOM node di dalam komponen Anda akan mempersulit untuk mengubah internal komponen Anda di kemudian hari. Anda biasanya akan mengekspos DOM node 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.
Example 1 of 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 daripada DOM node
Daripada mengekspos seluruh DOM node, Anda dapat mengekspos objek khusus, yang disebut imperative handle, dengan sekumpulan metode 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 DOM node. Hal ini memungkinkan Anda membatasi informasi yang Anda paparkan tentang DOM node 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 label="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 DOM node 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 node 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>
);
});