Apa perbedaan antara `useRef` dan` createRef`?

120

Saya sedang membaca dokumentasi hook ketika saya menemukan useRef.

Melihat contoh mereka…

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

… Sepertinya useRefbisa diganti dengan createRef.

function TextInputWithFocusButton() {
  const inputRef = createRef(); // what's the diff?
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputRef.current.focus();
  };
  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

Mengapa saya membutuhkan pengait untuk ref? Mengapa useRefada?

Rico Kahler
sumber

Jawaban:

153

Perbedaannya adalah createRefakan selalu membuat ref baru. Dalam komponen berbasis kelas, Anda biasanya akan meletakkan ref di properti instance selama konstruksi (misalnya this.input = createRef()). Anda tidak memiliki opsi ini di komponen fungsi. useRefmenangani pengembalian referensi yang sama setiap kali seperti pada rendering awal.

Berikut adalah contoh aplikasi yang mendemonstrasikan perbedaan perilaku kedua fungsi ini:

import React, { useRef, createRef, useState } from "react";
import ReactDOM from "react-dom";

function App() {
  const [renderIndex, setRenderIndex] = useState(1);
  const refFromUseRef = useRef();
  const refFromCreateRef = createRef();
  if (!refFromUseRef.current) {
    refFromUseRef.current = renderIndex;
  }
  if (!refFromCreateRef.current) {
    refFromCreateRef.current = renderIndex;
  }
  return (
    <div className="App">
      Current render index: {renderIndex}
      <br />
      First render index remembered within refFromUseRef.current:
      {refFromUseRef.current}
      <br />
      First render index unsuccessfully remembered within
      refFromCreateRef.current:
      {refFromCreateRef.current}
      <br />
      <button onClick={() => setRenderIndex(prev => prev + 1)}>
        Cause re-render
      </button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Edit 1rvwnj71x3

Ryan Cogswell
sumber
38

createRefselalu mengembalikan ref baru, yang biasanya Anda simpan sebagai bidang pada instance komponen kelas. useRefmengembalikan ref yang sama pada setiap render instance komponen fungsional. Inilah yang memungkinkan status ref tetap ada di antara render, meskipun Anda tidak menyimpannya secara eksplisit di mana pun.

Dalam contoh kedua Anda, referensi akan dibuat ulang pada setiap render.

Joe Clay
sumber
Ini tidak benar, apakah Anda memiliki referensi untuk mendukung pernyataan Anda?
Adeel Imran
1
Ada komentar di sini oleh salah satu pengembang React yang menjelaskan bahwa begitulah cara kerjanya: reddit.com/r/reactjs/comments/a2pt15/… Saya tertarik untuk mengetahui apa yang menurut Anda salah tentang jawaban ini.
Joe Clay
Saya melihat tautan itu sebelum saya mencoba menjawab pertanyaan ini, di manakah hal itu menyatakan fakta ini dalam tautan yang telah Anda bagikan? Saya tidak dapat menemukannya? :)
Adeel Imran
1
Tautan yang saya bagikan menunjukkan implementasi yang disederhanakan useRef, yang diposting oleh salah satu pengembang React. Ini tidak sama dengan hanya menelepon createRef, karena createRefbukan hook dan tidak mempertahankan status apa pun di antara panggilan. Jawaban Ryan Cogswell juga memiliki contoh perbedaan yang bagus.
Joe Clay
1
Pemahaman saya dari konteks itu hanya menyimpulkan bahwa useRef adalah hook kustom yang menggunakan createRef di dalamnya. Terima kasih telah berbagi ilmunya.
Adeel Imran
13

tldr

A refadalah objek JS biasa { current: <some value> }.

React.createRef()adalah pabrik yang mengembalikan wasit { current: null }- tidak melibatkan sihir .

useRef(initValue)juga mengembalikan referensi yang { current: initValue }mirip dengan React.createRef(). Selain itu, ia memoasi ref ini agar tetap ada di beberapa render dalam sebuah komponen fungsi .

Ini cukup untuk digunakan React.createRefdalam komponen kelas, karena objek ref ditugaskan ke variabel instan , sehingga dapat diakses di seluruh komponen dan gaya hidupnya:
this.myRef = React.createRef(); // stores ref in "mutable" this context (class)

useRef(null)pada dasarnya setara dengan useState(React.createRef())[0] 1 .


1 Gantikan useRefdengan useState+createRef

Berikut tweet yang telah mencerahkan bagi saya:

useRef()pada dasarnya useState({current: initialValue })[0].

Dengan wawasan dari tldrbagian tersebut, kita sekarang dapat menyimpulkan lebih lanjut:

useRef(null)pada dasarnya useState(React.createRef())[0].

Di atas kode "penyalahgunaan" useStateuntuk mempertahankan ref yang dikembalikan dari React.createRef(). [0]hanya memilih bagian nilaiuseState - [1]akan menjadi penyetel.

useStatemenyebabkan render ulang berbeda dengan useRef. Secara lebih formal, React membandingkan referensi objek lama dan baru useState, ketika nilai baru ditetapkan melalui metode penyetelnya. Jika kita mengubah status useStatesecara langsung (berlawanan dengan pemanggilan setter), perilakunya kurang lebih menjadi sama dengan useRef, karena tidak ada render ulang yang dipicu lagi:

// Example of mutaing object contained in useState directly
const [ref] = useState({ current: null })
ref.current = 42; // doesn't cause re-render

Catatan: Jangan lakukan ini! Gunakan useRefAPI yang dioptimalkan alih-alih menciptakan kembali roda. Di atas untuk tujuan ilustrasi.

ford04
sumber
3

Hanya untuk menyoroti tujuan:

createRefsesederhana itu return {current: null}. Ini adalah cara untuk menangani ref=prop dengan cara paling modern dan hanya itu (sementara berbasis string terlalu ajaib dan berbasis panggilan balik terlihat terlalu verboose).

useRefmenyimpan beberapa data sebelum merender dan mengubahnya tidak menyebabkan render ulang (seperti useStatehalnya). Mereka jarang berhubungan. Semua yang Anda harapkan untuk komponen berbasis kelas masuk ke bidang contoh ( this.* =) terlihat seperti kandidat untuk diimplementasikan useRefdalam komponen fungsional.

Katakanlah useCallbackbekerja sebagai metode kelas terbatas ( this.handleClick = .....bind(this)) dan dapat diimplementasikan kembali (tetapi kita tidak harus menemukan kembali roda untuk yakin) dengan useRef.

Contoh lain adalah referensi DOM, ID waktu tunggu / interval, pengenal atau referensi perpustakaan pihak ketiga.

PS Saya yakin tim React lebih baik memilih penamaan yang berbeda untuk useRefmenghindari kebingungan createRef. Mungkin useAndKeepatau bahkan usePermanent.

skyboyer
sumber
1

Namun tambahan lain tapi penting untuk jawaban orang lain.

Anda tidak dapat menetapkan nilai baru untuk createRef. Tapi Anda bisa untuk useRef.

const ur = useRef();
const cr = createRef();

ur.current = 10; // you can do it, and value is set
cr.current = 10; // you can, but it's no good, it will not change it
hijau
sumber
ref adalah objek biasa, Anda dapat mengubah currentpropertinya seperti biasa (baru saja mengujinya). Tidak masalah, jika ref dibuat melalui useRefatau createRef.
ford04