Statt ein useRef-Objekt als ref-Prop zu übergeben, kann man auch direkt eine Funktion angeben. React ruft diese Funktion mit dem DOM-Element auf, sobald die Komponente gemounted wurde, und ruft sie mit null auf, wenn die Komponente unmounted wird. Das nennt sich Callback Ref und ist ideal für Patterns, in denen man sofort auf Mount/Unmount reagieren will — ohne den Umweg über useEffect. Häufigster Anwendungsfall: Listen mit Pro-Item-Refs, weil man pro Item kein separates useRef aufrufen kann (Hook-Regeln). Mit React 19 gibt es ein neues Detail: der Callback kann optional eine Cleanup-Funktion zurückgeben.
Grundform
function FocusOnMount() {
return (
<input
ref={(node) => {
if (node) {
node.focus();
}
}}
/>
);
}Bei Mount ruft React (node) => ... mit dem <input>-Element auf, das fokussiert wird. Beim Unmount wird die Funktion mit null aufgerufen — wer da Cleanup braucht, prüft entsprechend.
Listen mit Pro-Item-Refs
Das wichtigste Anwendungsfeld: dynamische Listen, in denen pro Item eine Ref gebraucht wird (z.B. um zu scrollen).
import { useRef } from 'react';
export default function ScrollableList({ items }) {
const itemRefs = useRef(new Map());
const scrollTo = (id) => {
itemRefs.current.get(id)?.scrollIntoView({ behavior: 'smooth' });
};
return (
<>
<nav>
{items.map(item => (
<button key={item.id} onClick={() => scrollTo(item.id)}>
{item.title}
</button>
))}
</nav>
<ul>
{items.map(item => (
<li
key={item.id}
ref={(node) => {
if (node) {
itemRefs.current.set(item.id, node);
} else {
itemRefs.current.delete(item.id);
}
}}
>
{item.body}
</li>
))}
</ul>
</>
);
}Beim Mount jedes <li> wird der Knoten in die Map eingetragen. Beim Unmount wird er entfernt. Beim Klick auf einen Nav-Button wird zum passenden Item gescrollt.
Callback Ref vs. useRef
| Aspekt | useRef | Callback Ref |
|---|---|---|
| Form | <el ref={myRef} /> | <el ref={(node) => ...} /> |
| Mount-Reaktion | erst im useEffect | sofort im Callback |
| Unmount-Reaktion | im useEffect-Cleanup | direkt: Callback mit null |
| Mehrere Refs pro Komponente | mehrere useRef-Aufrufe | EIN useRef-Map + ein Callback |
| Stabilität pro Render | dasselbe Objekt | neue Funktion (Re-Trigger!) |
Vorteil von Callback Refs: sofort auf Mount reagieren, ohne useEffect-Roundtrip. Nachteil: weil die Funktion pro Render neu ist, ruft React sie bei jedem Render auf — einmal mit null (Unmount-Simulation), einmal mit dem Knoten (Mount-Simulation). Wer das vermeiden will: Funktion mit useCallback stabilisieren.
import { useCallback } from 'react';
export default function Stable() {
const setRef = useCallback((node) => {
if (node) {
console.log('Mount:', node);
}
}, []);
return <input ref={setRef} />;
}Jetzt läuft die Callback Ref nur beim echten Mount/Unmount.
React 19 — Cleanup-Funktion zurückgeben
Ab React 19 kann der Callback eine Cleanup-Funktion zurückgeben, ähnlich wie useEffect. React ruft sie beim Unmount auf — sauberer als das Null-Check-Pattern.
// React 19+
function FocusOnMount() {
return (
<input
ref={(node) => {
node.focus();
const handler = () => console.log('clicked');
node.addEventListener('click', handler);
return () => {
node.removeEventListener('click', handler);
};
}}
/>
);
}Pre-React-19 brauchte man das Null-Check-Pattern. In React 19 ist die Cleanup-Form deutlich klarer.
Beispiel — Auto-Größen-Tracking
import { useState, useCallback } from 'react';
export default function MeasureBox() {
const [size, setSize] = useState({ w: 0, h: 0 });
const measureRef = useCallback((node) => {
if (!node) return;
const update = () => {
const r = node.getBoundingClientRect();
setSize({ w: r.width, h: r.height });
};
update();
const observer = new ResizeObserver(update);
observer.observe(node);
// Pre-React-19: kein automatischer Cleanup — Observer leaks
// React 19+: return () => observer.disconnect();
}, []);
return (
<>
<div ref={measureRef} style={{ padding: 20 }}>Resize mich</div>
<p>Größe: {size.w} × {size.h}</p>
</>
);
}In React 19 mit returned Cleanup ist das ein perfektes Pattern. Pre-19 muss man auf Null-Check setzen und den Observer dort disconnecten.
Wann Callback Ref, wann useRef?
Callback Ref:
- Listen mit pro-Item-Refs (Hook-Regeln verbieten useRef in Schleifen).
- Sofortige Mount-Reaktion ohne useEffect-Indirection.
- Setup, das direkt auf das Element zugreifen muss (z.B. Listener oder Observer registrieren).
useRef + useEffect:
- Einzelne Ref auf ein Element, später Verwendung in Event-Handler.
- Wert soll zwischen Renders überleben (Timer-IDs, vorherige Werte).
- Ref ist Imperativ-Handle (
useImperativeHandle).
Besonderheiten
Callback Ref ist eine Funktion statt useRef-Objekt.
<el ref={(node) => ...} /> — React ruft die Funktion mit dem Element auf. Im Gegensatz zu useRef ist hier kein .current-Lookup nötig: das Element kommt direkt als Argument.
Pre-React-19: Null-Check für Mount/Unmount.
(node) => { if (node) setup(node); else cleanup(); } ist das klassische Pattern. Beim Mount kommt der Knoten, beim Unmount null. React 19 ersetzt das durch Return-Cleanup.
Inline-Callback wird pro Render neu erstellt — Re-Trigger.
Wenn die Funktion bei jedem Render neu ist, ruft React sie auf jedem Render auf (mit null, dann mit dem Knoten). Lösung: useCallback stabilisieren, dann läuft sie nur beim echten Mount/Unmount.
Listen-Pattern: useRef-`Map` + Callback Ref pro Item.
Pro-Item-useRef in einer map-Schleife wäre Hook-Verletzung. Lösung: EIN useRef mit Map als Container, pro Item ein Callback Ref, der Items rein und raus mappt.
React 19: Return-Funktion ist Cleanup beim Unmount.
Wie bei useEffect: die zurückgegebene Funktion läuft beim Unmount. Saubere Resource-Disposal ohne den Null-Check-Tanz.
Callback Ref kann mehrere Sachen tun — Listener, Observer, Setup.
Pre-19 hat man dafür manchmal mehrere useEffects gebaut. Callback Ref bündelt das in einer Funktion, die direkt mit dem Element arbeitet.
TypeScript: (node: HTMLDivElement | null) => void.
Bei Pre-19-Callback-Refs ist der Parameter-Typ ElementType | null. Bei React 19 mit Cleanup: (node: ElementType) => (() => void) | void.
Callback Ref + useRef-Objekt kombinieren via `mergeRefs`.
Wenn eine Komponente sowohl forwardRef (mit ref von außen) als auch ein eigenes Callback Ref braucht, gibt's Helper wie useMergedRefs (kleine Library) oder eine eigene Merge-Funktion, die beide bedient.
Weiterführende Ressourcen
Externe Quellen
- ref callback function – react.dev
- Ref callbacks with cleanup – React 19 Blog
- How to manage a list of refs using a ref callback – react.dev