
Cara cepat memulai VUE3.0: Masuk dan pelajari
Dalam proyek React, ada banyak skenario dimana Ref diperlukan. Misalnya, gunakan atribut ref untuk mendapatkan node DOM dan mendapatkan instance objek ClassComponent; gunakan useRef Hook React.createRef membuat objek Ref untuk menyelesaikan masalah setInterval yang tidak dapat memperoleh status terbaru; metode untuk membuat objek Ref secara manual.
Meskipun Ref sangat mudah digunakan, namun tetap saja menemui masalah dalam proyek sebenarnya. Artikel ini akan menyelesaikan berbagai masalah terkait Ref dari perspektif kode sumber dan memperjelas apa yang dilakukan di balik API terkait ref . Setelah membaca artikel ini, Anda mungkin memiliki pemahaman lebih dalam tentang Ref .
Pertama-tama, ref adalah singkatan dari reference , yang merupakan referensi. Dalam file deklarasi tipe react , Anda dapat menemukan beberapa tipe terkait Ref, dan tipe tersebut tercantum di sini.
RefObject<T> { arus baca saja: T |.
interface MutableRefObject<T> { current: T; } Saat menggunakan useRef Hook , RefObject /MutableRefObejct dikembalikan. Kedua tipe mendefinisikan struktur objek { current: T } , TypeScript akan memperingatkan ⚠️ jika refObject.current diubah.
const ref = useRef<string>(null) ref.current = '' // Kesalahan
TS: Tidak dapat ditetapkan ke "saat ini" karena ini adalah properti hanya-baca.

Lihat definisi metode useRef . Fungsi kelebihan beban digunakan di sini. Jika parameter generik MutableRefObject<T> masuk T tidak berisi null , null RefObject<T> dikembalikan.
fungsi useRef<T>(initialValue: T): MutableRefObject<T>; function useRef<T>(initialValue: T | null): RefObject<T>;
Jadi, jika Anda ingin properti objek ref yang dibuat saat ini dapat dimodifikasi, Anda perlu menambahkan | null .
const ref = gunakanRef<string |.null>(null) ref.current = '' // Oke,
saat memanggil metode React.createRef() , RefObject juga dikembalikan.
createRef
createRef(): RefObject {
const refObject = {
saat ini: nol,
};
jika (__DEV__) {
Objek.seal(refObject);
}
kembalikan refObject;
} RefObject/MutableRefObject ditambahkan di versi 16.3 . Jika Anda menggunakan versi sebelumnya, Anda perlu menggunakan Ref Callback .
Menggunakan Ref Callback adalah meneruskan fungsi panggilan balik. Saat reaksi memanggil kembali, instance terkait akan diteruskan kembali, dan dapat disimpan dengan sendirinya untuk dipanggil. Jenis fungsi panggilan balik ini adalah RefCallback .
ketik RefCallback<T> = (contoh: T | null) =>
RefCallback
;
kelas ekspor CustomTextInput memperluas React.Component {
masukan teks: HTMLInputElement |.null = null;
saveInputRef = (elemen: HTMLInputElement | null) => {
this.textInput = elemen;
}
memberikan() {
kembali (
<input type="text" ref={ini.saveInputRef} />
);
}
} Dalam deklarasi tipe, terdapat juga tipe Ref/LegacyRef, yang digunakan untuk merujuk pada tipe Ref secara umum. LegacyRef adalah versi yang kompatibel. Pada versi lama sebelumnya, ref juga bisa berupa string.
ketik Ref<T> = RefCallback<T> |.RefObject<T> |. type LegacyRef<T> = string |.Ref<T>;
Hanya dengan memahami tipe-tipe yang terkait dengan Ref Anda dapat menjadi lebih nyaman menulis TypeScript.
MelewatiSaat menggunakan ref pada komponen JSX, kita menyetel Ref ke atribut ref . Kita semua tahu bahwa sintaks jsx akan dikompilasi ke dalam bentuk createElement dengan alat seperti Babel.
//jsx
<Ref Aplikasi={ref} id="aplikasi-saya" ></Aplikasi>
// dikompilasi ke
React.createElement(Aplikasi, {
referensi: referensi,
id: "aplikasi saya"
}); Tampaknya ref tidak berbeda dengan props lainnya, tetapi jika Anda mencoba mencetak props.ref di dalam komponen, hasilnya adalah undefined . Dan konsol lingkungan dev akan memberikan petunjuk.
Mencoba mengaksesnya akan menghasilkan
undefinedyang dikembalikan. Jika Anda perlu mengakses nilai yang sama dalam komponen anak, Anda harus meneruskannya sebagai prop yang berbeda.
Apa yang dilakukan React dengan ref? Seperti yang Anda lihat di kode sumber ReactElement, ref adalah RESERVED_PROPS . key juga memiliki perlakuan ini. Kunci tersebut akan diproses dan diekstraksi secara khusus dari props dan diteruskan ke Element .
const RESERVED_PROPS = {
kunci: benar,
referensi: benar,
__diri: benar,
__sumber: benar,
}; Jadi ref adalah “props“ yang akan diperlakukan secara khusus.
Sebelum versi 16.8.0 , Komponen Fungsi tidak memiliki kewarganegaraan dan hanya akan dirender berdasarkan props yang masuk. Dengan Hook, Anda tidak hanya dapat memiliki status internal, tetapi juga mengekspos metode untuk panggilan eksternal (memerlukan forwardRef dan useImperativeHandle ).
Jika Anda menggunakan ref secara langsung untuk Function Component , konsol di lingkungan dev akan memperingatkan Anda bahwa Anda perlu membungkusnya dengan forwardRef .
fungsiMasukan() {
kembali <masukan />
}
const ref = gunakanRef()
<Input ref={ref} /> Komponen fungsi tidak dapat diberikan referensi. Upaya untuk mengakses referensi ini akan gagal. Apakah Anda bermaksud menggunakan React.forwardRef()
forwardRef Lihat kode sumber ReactForwardRef.js. Lipat kode terkait __DEV__ Ini hanyalah komponen tingkat tinggi yang sangat sederhana. Terima FunctionComponent yang dirender, bungkus dan definisikan $$typeof sebagai REACT_FORWARD_REF_TYPE , lalu return .

Lacak kodenya dan temukan resolusiLazyComponentTag, tempat $$typeof akan diurai ke dalam WorkTag yang sesuai.

WorkTag yang sesuai dengan REACT_FORWARD_REF_TYPE adalah ForwardRef. Kemudian ForwardRef akan masuk ke logika updateForwardRef.
kasus MajuRef: {
anak = updateForwardRef(
batal,
pekerjaan Sedang Berlangsung,
Komponen,
alat peraga terselesaikan,
renderLanes,
);
kembalinya anak;
} Metode ini akan memanggil metode renderWithHooks dan meneruskan ref pada parameter kelima.
berikutnyaAnak-anak = renderWithHooks( saat ini, pekerjaan Sedang Berlangsung, memberikan, alat peraga berikutnya, ref, // di sini renderLanes, );
Lanjutkan menelusuri kode dan masukkan metode renderWithHooks. Anda dapat melihat bahwa ref akan diteruskan sebagai parameter kedua dari Component . Pada titik ini kita dapat memahami dari mana ref parameter kedua dari FuncitonComponent yang dibungkus oleh forwardRef (dibandingkan dengan parameter kedua konstruktor ClassComponent yaitu Konteks).

Mengetahui cara mengoper wasit, pertanyaan selanjutnya adalah bagaimana cara pemberian wasit.
(tetapkan RefCallback ke ref dan break point di callback). Lacak kode commitAttachRef. Dalam metode ini, akan dinilai apakah ref dari node Fiber adalah function atau RefObject, dan instance akan diproses sesuai jenisnya. Jika node Fiber adalah HostComponent ( tag = 5 ), yang merupakan node DOM, instance adalah node DOM; dan jika node Fiber adalah ClassComponent ( tag = 1 ), instance adalah instance objek.
fungsi commitAttachRef(selesaiPekerjaan) {
var ref = pekerjaan selesai.ref;
jika (ref !== nol) {
var instanceToUse = finishWork.stateNode;
if (typeof ref === 'fungsi') {
ref(instanceToUse);
} kalau tidak {
ref.saat ini = instanceToUse;
}
}
} Di atas adalah logika penugasan ref di HostComponent dan ClassComponent. Untuk komponen tipe ForwardRef, kode yang digunakan berbeda, tetapi perilakunya pada dasarnya sama. Anda dapat melihat imperatifHandleEffect di sini.
Selanjutnya, kita terus menggali kode sumber React untuk melihat bagaimana useRef diimplementasikan.
menemukan kode runtime useRef ReactFiberHooks dengan melacak kode tersebut

Ada dua metode di sini, mountRef dan updateRef . Seperti namanya, keduanya sesuai dengan operasi pada ref saat node Fiber mount dan update .
fungsi updateRef<T>(Nilai Awal: T): {|saat ini: T|} {
const hook = updateWorkInProgressHook();
kembalikan hook.memoizedState;
}
function mountRef<T>(initialValue: T): {|saat ini: T|} {
const hook = mountWorkInProgressHook();
const ref = {saat ini: nilai awal};
hook.memoizedState = ref;
kembalikan referensi;
} Anda dapat melihat bahwa ketika mount , useRef membuat RefObject dan menugaskannya ke memoizedState hook Saat update , ia dikeluarkan dan dikembalikan secara langsung.
Hook yang berbeda memoizedState menyimpan konten yang berbeda. useState menyimpan informasi state , useEffect menyimpan objek effect , useRef menyimpan objek ref ...
Metode mountWorkInProgressHook dan updateWorkInProgressHook didukung oleh daftar Hooks yang tertaut mengambil objek memoizedState yang sama setiap kali Anda merender useRef Sesederhana itu.
Pada titik ini, kita memahami logika meneruskan dan menetapkan ref di React, serta kode sumber yang terkait dengan useRef . Gunakan pertanyaan aplikasi untuk menggabungkan poin pengetahuan di atas: Ada komponen Input. Di dalam komponen, innerRef HTMLInputElement perlu digunakan untuk mengakses node DOM untuk menerapkannya?
const Masukan = forwardRef((alat peraga, ref) => {
const innerRef = useRef<HTMLInputElement>(null)
kembali (
<masukan {...alat peraga} ref={???} />
)
}) Perhatikan bagaimana penulisan ??? pada kode di atas.
============ Garis pemisah jawaban ==============
Dengan memahami implementasi internal terkait Ref, jelas kita dapat membuat RefCallback di sini, yang dapat menangani banyak. Cukup tetapkan ref .
fungsi ekspor menggabungkanRefs<T = any>(
referensi: Array<MutableRefObject<T |.null> |.RefCallback<T>>
): React.RefCallback<T> {
nilai kembalian => {
refs.forEach(ref => {
if (typeof ref === 'fungsi') {
ref(nilai);
} lain jika (ref !== null) {
ref.saat ini = nilai;
}
});
};
}
const Masukan = forwardRef((alat peraga, ref) => {
const innerRef = useRef<HTMLInputElement>(null)
kembali (
<input {...props} ref={combineRefs(ref, innerRef)} />
)
})