Was ist der Unterschied zwischen "useRef" und "createRef"?

101

Ich habe gerade die Hooks-Dokumentation durchgesehen, als ich darauf gestoßen bin useRef.

Schauen Sie sich ihr Beispiel an…

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

… Es scheint, useRefals könnte durch ersetzt werden 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>
    </>
  );
}

Warum brauche ich einen Haken für Schiedsrichter? Warum gibt useRefes?

Rico Kahler
quelle

Antworten:

130

Der Unterschied ist, dass createRefimmer eine neue Referenz erstellt wird. In einer klassenbasierten Komponente setzen Sie den Verweis normalerweise während der Erstellung in eine Instanzeigenschaft (z this.input = createRef(). B. ). Sie haben diese Option in einer Funktionskomponente nicht. useRefsorgt dafür, dass jedes Mal dieselbe Referenz wie beim ersten Rendern zurückgegeben wird.

Hier ist eine Beispiel-App, die den Unterschied im Verhalten dieser beiden Funktionen demonstriert:

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

Bearbeiten Sie 1rvwnj71x3

Ryan Cogswell
quelle
34

createRefGibt immer eine neue Referenz zurück, die Sie normalerweise als Feld in der Instanz einer Klassenkomponente speichern würden. useRefkehrt die gleiche ref auf jeden übertragen von einer funktionalen Komponente der Instanz. Dies ermöglicht es dem Status des Schiedsrichters, zwischen den Renderings bestehen zu bleiben, obwohl Sie ihn nirgendwo explizit speichern.

In Ihrem zweiten Beispiel wird der Verweis bei jedem Rendern neu erstellt.

Joe Clay
quelle
Dies ist falsch. Haben Sie einen Verweis, um Ihre Aussage zu sichern?
Adeel Imran
Es gibt hier einen Kommentar von einem der React-Entwickler, der erklärt, dass dies so funktioniert: reddit.com/r/reactjs/comments/a2pt15/… Ich würde gerne wissen, was Ihrer Meinung nach an dieser Antwort falsch ist.
Joe Clay
Ich habe diesen Link gesehen, bevor ich versucht habe, diese Frage zu beantworten. Wo steht diese Tatsache in dem Link, den Sie geteilt haben? Ich konnte es nicht finden? :)
Adeel Imran
1
Der Link, den ich geteilt habe, zeigt eine vereinfachte Implementierung von useRef, die von einem der React-Entwickler veröffentlicht wurde. Es ist nicht dasselbe wie einfach anzurufen createRef, da createRefes kein Hook ist und keinen Status zwischen Anrufen beibehält. Die Antwort von Ryan Cogswell hat auch ein gutes Beispiel für die Unterschiede.
Joe Clay
1
Mein Verständnis aus diesem Kontext ergab nur, dass useRef ein benutzerdefinierter Hook ist, der createRef verwendet. Vielen Dank für das Teilen des Wissens.
Adeel Imran
5

tldr

A refist ein einfaches JS-Objekt { current: <some value> }.

React.createRef()ist eine Fabrik, die einen Schiedsrichter zurückgibt { current: null }- ohne Magie .

useRef(initValue)gibt auch einen { current: initValue }ähnlichen Ref zurück React.createRef(). Außerdem wird dieser Verweis so gespeichert , dass er über mehrere Renderings in einer Funktionskomponente hinweg beständig ist .

Die Verwendung React.createRefin Klassenkomponenten ist ausreichend , da das ref-Objekt einer Instanzvariablen zugewiesen ist , auf die über die gesamte Komponente und ihren Lebenszyklus zugegriffen werden kann:
this.myRef = React.createRef(); // stores ref in "mutable" this context (class)

useRef(null)ist im Grunde gleich useState(React.createRef())[0] 1 .


1 Ersetzen Sie useRefdurch useState+createRef

Der folgende Tweet war für mich aufschlussreich:

useRef()ist im Grunde useState({current: initialValue })[0].

Mit den Erkenntnissen aus dem tldrAbschnitt können wir nun weiter schließen:

useRef(null)ist im Grunde useState(React.createRef())[0].

Der obige Code "missbraucht", useStateum die zurückgegebene Referenz von beizubehalten React.createRef(). [0]wählt einfach den Werteteil von useState- [1]wäre der Setter.

useStatebewirkt im Gegensatz zu ein erneutes Rendern useRef. Genauer gesagt vergleicht React die alte und die neue Objektreferenz useState, wenn ein neuer Wert über seine Setter-Methode festgelegt wird. Wenn wir mutieren den Zustand useStatedirekt ( im Gegensatz zu Setter Aufruf), ihr Verhalten mehr oder weniger wird äquivalent zu useRef, da keine Wieder machen mehr ausgelöst wird:

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

Hinweis: Tun Sie das nicht! Verwenden Sie die optimierte useRefAPI, anstatt das Rad neu zu erfinden. Oben dient zur Veranschaulichung.

ford04
quelle
3

Nur um einen Zweck hervorzuheben:

createRefist so einfach wie return {current: null}. Es ist eine Möglichkeit, mit ref=Requisiten auf modernste Weise umzugehen, und das war's auch (während String-basiert zuoo magisch und Callback-basiert zu ausführlich aussieht).

useRefBehält einige Daten vor dem Rendern bei und das Ändern führt nicht zu einem erneuten Rendern (wie dies auch der useStateFall ist). Sie sind selten verwandt. Alles, was Sie von einer klassenbasierten Komponente erwarten, die in Instanzfelder ( this.* =) verschoben wird, sieht aus wie ein Kandidat, der useRefin funktionale Komponenten implementiert werden soll.

Say useCallbackfunktioniert als beschränkte Klassenmethode ( this.handleClick = .....bind(this)) und kann mit neu implementiert werden (aber wir sollten das Rad nicht sicher neu erfinden) mit useRef.

Ein weiteres Beispiel sind DOM-Refs, Timeout- / Intervall-IDs, IDs oder Referenzen von Bibliotheken von Drittanbietern.

PS Ich glaube, das React-Team hat besser unterschiedliche Namen gewählt useRef, um Verwechslungen mit zu vermeiden createRef. Vielleicht useAndKeepoder sogar usePermanent.

Skyboyer
quelle
1

Eine weitere, aber wichtige Ergänzung zu den Antworten anderer.

Sie können keinen neuen Wert für festlegen createRef. Aber du kannst für 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
Grün
quelle
ref ist ein einfaches Objekt, Sie können seine currentEigenschaft wie gewohnt ändern (gerade getestet). Es spielt keine Rolle, ob der Ref über useRefoder erstellt wird createRef.
ford04