useRef
Etwas merken, ohne dass React davon aufwacht
useRef ist eine kleine Kiste, die React zwischen den Durchläufen aufhebt. Etwas hineinzulegen löst kein Neuzeichnen aus – genau dafür ist sie da.
const kiste = useRef(startwert);
kiste.current // hineinschauen
kiste.current = neu; // hineinlegen -> React zeichnet NICHT neu useState ist eine Anzeigetafel: Schreibst du darauf, schauen alle hin. useRef ist ein Zettel in der Hosentasche: Er bleibt dir erhalten, aber niemand merkt, wenn du ihn änderst.
Zweck 1: ein Element auf der Seite anfassen
Manchmal musst du dem Browser direkt etwas sagen: „setz den Cursor hierhin“, „scroll dorthin“, „spiel das Video ab“. Dafür brauchst du das echte Element – und genau das legt React für dich in die Kiste.
function Suche() {
const inputRef = useRef<HTMLInputElement>(null);
return (
<>
<input ref={inputRef} />
<button onClick={() => inputRef.current?.focus()}>Fokus</button>
</>
);
} Beim ersten Durchlauf existiert das Element noch gar nicht – React baut es ja erst danach. Deshalb greifst du mit ?. zu oder erst in einem Klick-Handler.
Zweck 2: still etwas merken
// Die Nummer eines Timers, um ihn später abbrechen zu können
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
function beiEingabe() {
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = setTimeout(suchen, 500);
}
// Wie oft wurde gezeichnet? (nur zum Anschauen, nicht zum Anzeigen)
const zaehler = useRef(0);
zaehler.current += 1; State oder Ref? Eine einzige Frage
const countRef = useRef(0);
function klick() {
countRef.current += 1;
}
return <p>{countRef.current}</p>; const [count, setCount] = useState(0);
function klick() {
setCount((c) => c + 1);
}
return <p>{count}</p>; Links steigt die Zahl in der Kiste tatsächlich – nur zeichnet React nicht neu, also siehst du immer die 0. Sichtbar → State. Unsichtbar → Ref.
- +
Ein Element anfassen:
focus(), scrollen, messen, Video steuern - +
Timer-Nummern aufbewahren
- +
Den vorherigen Wert von etwas merken
- +
Objekte fremder Bibliotheken halten (Karte, Diagramm, Editor)
- −
Werte, die auf dem Bildschirm erscheinen – das ist State
- −
Als Trick, um Neuzeichnen zu „sparen“ – dann friert die Anzeige ein
- −
Lesen oder Schreiben mitten im Zeichnen
Lies und schreib Refs in Klick-Handlern oder in useEffect – nicht direkt im Funktionskörper. Sonst hängt das Ergebnis davon ab, wann React zufällig zeichnet, und das kann React nicht garantieren.
▸ Genauer erklärt: Callback-Refs und ref als Prop optional
Statt einer Kiste kannst du dem ref-Attribut auch eine Funktion geben. React ruft sie auf, sobald das Element auf die Seite kommt – und noch einmal, wenn es verschwindet. Praktisch, wenn du direkt danach etwas messen willst.
function Box() {
const [hoehe, setHoehe] = useState(0);
return (
<div
ref={(node) => {
if (node) setHoehe(node.getBoundingClientRect().height);
// React 19: was du hier zurückgibst, dient als Aufräumfunktion
}}
>
…
</div>
);
} ref ist inzwischen eine ganz normale Prop: function Input({ ref, ...rest }) { … }. In älterem Code siehst du dafür noch forwardRef – das funktioniert weiterhin, ist aber nicht mehr nötig.
Sitzt das schon?
4 Fragen zu dieser Lektion. Falsche Antworten landen in deiner Statistik.
Jetzt selbst schreiben
Zwei Aufgaben: Der Button soll den Cursor ins Eingabefeld setzen, und daneben soll mitgezählt werden, wie oft die Komponente gezeichnet wurde – ohne dass das Zählen selbst ein Zeichnen auslöst.
- □Klick auf
Fokussetzt den Cursor ins Feld - □Die Zahl bei
Renderssteigt beim Tippen - □Das Mitzählen selbst löst kein zusätzliches Zeichnen aus
import { useRef, useState } from "react"; export default function App() { const [text, setText] = useState(""); // TODO: Ref für das Eingabefeld // TODO: Ref für den Zähler return ( <div style={{ fontFamily: "system-ui", padding: 16 }}> <input aria-label="feld" value={text} onChange={(e) => setText(e.target.value)} />{" "} <button onClick={() => { /* TODO: fokussieren */ }}>Fokus</button> <p data-testid="renders">Renders: 0</p> </div> ); }