useRef gibt ein Objekt zurück, { current: initialValue }, das React für die ganze Lebensdauer der Komponente behält. ref.current zu ändern rendert nichts neu. Damit sind Refs für zwei Aufgaben richtig: einen DOM-Knoten halten, um Methoden wie focus() darauf aufzurufen, und sich zwischen Renders einen Wert merken, den der Bildschirm nicht zeigen muss.
Das Ref-Objekt an ref={inputRef} zu übergeben sagt React, den DOM-Knoten des <input> in inputRef.current zu legen. Ändere focus() zu select(), tippe etwas und klicke erneut: Der Text wird markiert.
Die Syntax
const myRef = useRef(initialValue);
myRef.current; // read
myRef.current = value; // write, no re-render
initialValuewird nur beim ersten Render verwendet. Bei DOM-Refs ist ernull.- React gibt bei jedem Render dasselbe Objekt zurück. Nur
currentändert sich. currentzu lesen und zu schreiben ist gewöhnliches JavaScript. React verfolgt es nicht.
Ein Wert, der keine Renders auslöst
State ist für das, was der Bildschirm zeigt. Eine Ref ist für das, was sich die Komponente merken, aber nicht anzeigen muss. Dieser Block hält einen Zähler in einer Ref und einen im State und loggt beide:
Klicke ein paar Mal auf Ref + 1. Die Konsole zeigt, wie ref.current steigt, aber die Seite zeigt weiterhin 0, und kein Render wird geloggt. Klicke jetzt auf State + 1: Die Komponente rendert, und der Ref-Zähler auf dem Bildschirm springt auf die Zahl, die die Ref schon hielt. Die Ref hat ihren Wert die ganze Zeit behalten; React hatte nur keinen Grund, ihn zu zeichnen.
Refs vs. State
useState | useRef | |
|---|---|---|
| Gibt zurück | [value, setValue] | { current: value } |
| Ändern | Rendert die Komponente erneut | Bewirkt nichts Sichtbares |
| Wann der neue Wert lesbar ist | Im nächsten Render | Sofort |
| Ändern mit | Dem Setter | Zuweisung, ref.current = x |
| Nutzen für | Alles, was der Bildschirm zeigt | Timer-IDs, DOM-Knoten, vorherige Werte, Flags |
Wenn du unsicher bist, frag dich, ob das JSX den Wert liest. Wenn ja, gehört er in den State.
Eine Timer-ID speichern
setInterval gibt eine ID zurück, die du später brauchst, um den Timer zu stoppen. Sie passt gut in eine Ref: Der Stop-Handler braucht sie, aber der Bildschirm zeigt sie nie, und in einer einfachen Variablen ginge sie beim nächsten Render verloren.
Ersetze useRef(null) durch let intervalRef = { current: null };, probiere Start, warte zwei Sekunden und klicke dann auf Stop: Der Timer läuft weiter. Jeder Tick rendert die Komponente, was ein frisches Objekt erzeugt, also löscht Stop nichts.
Den vorherigen Wert merken
Ein Effekt läuft nach dem Render, also kann er den neuen Wert mit dem in einer Ref gespeicherten vergleichen und dann den neuen für das nächste Mal sichern:
Klicke auf green, dann auf blue: Die Konsole loggt changed from red to green und changed from green to blue. Ein Klick auf die bereits gewählte Farbe loggt nichts, weil der Effekt nur läuft, wenn sich color ändert.
Mit dem DOM arbeiten
Refs sind der Weg, Browser-APIs aufzurufen, für die React keine Prop hat: focus(), select(), scrollIntoView(), play() an einem Video oder das Lesen einer Größe mit getBoundingClientRect().
const lastItemRef = useRef(null);
function scrollToLast() {
lastItemRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
<li ref={lastItemRef}>Last item</li>
ref.current ist beim ersten Render null, weil React den DOM-Knoten noch nicht erzeugt hat, und nach dem Entfernen des Elements wieder. Lies ihn in Event-Handlern und Effekten, wo der Knoten existiert. Um ein Element zu messen und etwas zu positionieren, bevor der Browser zeichnet, lies die Ref in useLayoutEffect.
ref.current nicht beim Rendern lesen oder schreiben
React erwartet, dass der Komponentenkörper eine pure Berechnung ist: gleiche Props und gleicher State, gleiches JSX. Eine Ref ändert sich außerhalb dieses Modells, also kann ein Render, der sie liest oder schreibt, für dieselben Eingaben unterschiedliche Ergebnisse zeigen, und React kann den Körper öfter aufrufen, als du erwartest.
function Bad() {
const count = useRef(0);
count.current++; // writing during render
return <p>{count.current}</p>; // reading during render
}
Lies und schreibe Refs in Event-Handlern und Effekten. Die einzige Ausnahme ist die lazy Initialisierung, bei der du die Ref einmal beim ersten Render füllst:
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = new VideoPlayer(); // runs once
}
(Die Ref-Zähler-Demo oben liest clicks.current absichtlich im JSX, um zu zeigen, dass der Bildschirm ihm nicht folgt. Übernimm das nicht in echten Code.)
Ref-Callbacks
Statt eines Ref-Objekts kannst du ref eine Funktion übergeben. React ruft sie mit dem DOM-Knoten auf, wenn das Element hinzugefügt wird. In React 19 kann die Funktion ein Cleanup zurückgeben, das React aufruft, wenn das Element entfernt wird. Das ist praktisch für eine Liste, in der du useRef nicht einmal pro Eintrag aufrufen kannst:
Beim Laden loggt die Konsole eine Zeile attached pro Eintrag. Klicke auf Remove last, und nur detached Cheese erscheint. trackNode ist außerhalb der Komponente definiert, also ist es bei jedem Render dieselbe Funktion, und React lässt die anderen Einträge in Ruhe. Schreib es stattdessen inline als ref={(node) => { ... }}, und jeder Klick löst jeden Eintrag und hängt ihn wieder an, weil jeder Render eine neue Funktion übergibt.
Eine Ref an deine eigene Komponente übergeben
In React 19 ist ref für Funktionskomponenten eine normale Prop. Das Elternteil erzeugt die Ref, und das Kind setzt sie auf das DOM-Element, das es freigeben will:
Vor React 19 brauchte das forwardRef, das weiterhin funktioniert. Die Seite zu forwardRef zeigt beide Versionen und wie du mit useImperativeHandle nur ein paar Methoden freigibst.
Typische Fehler
Einen Ref-Wert im JSX anzeigen. Der Bildschirm folgt ihm nicht. Wenn der Wert sichtbar sein soll, nutze State.
ref.current als Abhängigkeit eines Effekts nutzen. [ref.current] liest den Wert beim Rendern, und React weiß nicht, wann er sich ändert, also läuft der Effekt nicht erneut, wenn er es tut. Nutze einen Ref-Callback, um darauf zu reagieren, dass ein Knoten angehängt wird.
Eine DOM-Ref beim ersten Render lesen. Sie ist null, bis React den Knoten erzeugt hat. Lies sie in einem Effekt oder einem Handler.
Ein neues Objekt erzeugen, statt den Hook zu nutzen. const ref = { current: null } im Komponentenkörper ist bei jedem Render ein frisches Objekt. Nur useRef gibt dir jedes Mal dasselbe.
Häufig gestellte Fragen
Was gibt useRef zurück?
Ein einfaches Objekt mit einer Eigenschaft, current, die auf den übergebenen Anfangswert gesetzt ist. React gibt bei jedem Render dasselbe Objekt zurück, also ist alles, was du in current legst, beim nächsten Mal noch da.
Was ist der Unterschied zwischen useRef und useState?
State zu ändern rendert die Komponente erneut; ref.current zu ändern nicht. Nutze State für Werte, die der Bildschirm zeigt, und eine Ref für Werte, die nur deine Event-Handler und Effekte brauchen, etwa eine Timer-ID oder einen DOM-Knoten.
Warum ist ref.current null?
React setzt ref.current auf den DOM-Knoten, nachdem es den Knoten erzeugt hat, während des Commits. Beim ersten Render ist er noch null, und nachdem das Element entfernt wurde, wieder null. Lies ihn in einem Event-Handler oder einem Effekt.
Löst das Ändern von ref.current ein erneutes Rendern aus?
Nein. React weiß nicht, dass du ihn geändert hast. Der neue Wert erscheint nur dann auf dem Bildschirm, wenn etwas anderes, etwa ein State-Update, die Komponente erneut rendert.
Wie übergebe ich eine Ref an eine Kindkomponente?
In React 19 erhält eine Funktionskomponente ref als normale Prop, also übergibst du <MyInput ref={inputRef} />, und das Kind setzt sie auf sein <input>. Ältere Versionen brauchten dafür forwardRef.