forwardRef und Ref-Weiterreichen in wiederverwendbaren Komponenten
AI generated
{ }
React 19 · Refs · Komponentenbibliotheken
forwardRef und Ref-Weiterreichen
In wiederverwendbaren Komponenten

Ein Ref auf eine selbst geschriebene Komponente funktioniert standardmäßig nicht, weil React Refs nicht automatisch durch Komponentengrenzen weiterreicht. forwardRef und useImperativeHandle lösen das gezielt, ohne die Kapselung der Komponente aufzugeben.

13 Min. Lesezeit Refs DOM-Zugriff Komponenten-APIs

1. Das Grundproblem: Refs stoppen an der Komponentengrenze

Ein ref, der direkt an ein natives DOM-Element wie <input /> übergeben wird, funktioniert immer, weil React Refs bei nativen Host-Elementen automatisch mit der zugrundeliegenden DOM-Node verknüpft. Übergibt man denselben ref jedoch an eine selbst geschriebene Funktionskomponente, etwa eine wiederverwendbare TextField-Komponente, funktioniert das ohne Weiteres nicht: React kennt bei Funktionskomponenten schlicht kein eingebautes Verfahren, den ref automatisch an ein inneres Element weiterzureichen.

Der Grund liegt darin, dass props und ref in React konzeptionell getrennt behandelt werden. Eine Funktionskomponente erhält ref nicht einfach als weiteren Eintrag im props-Objekt, weil das die Bedeutung von Refs verwässern würde, jede Komponente könnte sonst beliebige, undurchsichtige Dinge mit einem als Prop getarnten Ref anstellen. Stattdessen muss eine Komponente explizit signalisieren, dass und wie sie Refs nach innen weiterreicht.

2. Wie sich das Problem ohne forwardRef zeigt

Versucht man, einen ref direkt an eine gewöhnliche Funktionskomponente zu übergeben, gibt React eine Warnung in der Konsole aus, dass Funktionskomponenten standardmäßig keine Refs entgegennehmen können, und der ref bleibt null. Für eine Komponente, die intern etwa einen fokussierbaren <input> rendert, bedeutet das, dass die aufrufende Seite keine Möglichkeit hat, programmatisch den Fokus auf dieses Feld zu setzen, obwohl genau das ein legitimer, häufiger Anwendungsfall ist.

Das Fehlerbild ist besonders in Formular-lastigen Anwendungen relevant, wo etwa nach einer Validierungsfehlermeldung automatisch das erste fehlerhafte Feld fokussiert werden soll. Ohne eine Möglichkeit, den ref gezielt weiterzureichen, müsste diese Logik entweder komplett innerhalb der Wrapper-Komponente selbst gelöst werden, was die Wiederverwendbarkeit einschränkt, oder die Kapselung der Komponente aufgegeben werden.


function TextField(props) {
  return <input {...props} />;
}

function Form() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current?.focus(); // funktioniert NICHT
  }, []);

  // Warnung: Function components cannot be given refs
  return <TextField ref={inputRef} />;
}

3. Die Lösung: forwardRef macht den Ref explizit steuerbar

forwardRef() umschließt eine Funktionskomponente und gibt ihr einen zweiten Parameter neben props, nämlich ref. Innerhalb der Komponente entscheidet man dann selbst, an welches innere Element dieser ref weitergereicht wird, meist direkt an das native DOM-Element, das die Komponente kapselt. Damit funktioniert die aufrufende Seite exakt so, wie man es von einem nativen Element erwarten würde.

Wichtig ist, dass forwardRef die Kapselung der Komponente nicht aufhebt, sondern bewusst öffnet: Die Komponente entscheidet aktiv, welches interne Element über den Ref erreichbar ist, nicht die aufrufende Seite. Das unterscheidet forwardRef fundamental von einem direkten DOM-Zugriff über document.querySelector, der die Komponentenkapselung komplett umgehen würde.


const TextField = forwardRef(function TextField(props, ref) {
  return <input ref={ref} {...props} />;
});

function Form() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current?.focus(); // funktioniert jetzt korrekt
  }, []);

  return <TextField ref={inputRef} />;
}

4. Typische Einsatzfälle in Komponentenbibliotheken

In wiederverwendbaren UI-Komponentenbibliotheken ist forwardRef nahezu Standard, weil Nutzer solcher Bibliotheken erwarten, dass sich eigene Wrapper-Komponenten wie native Elemente verhalten. Ein Button, ein TextField oder ein Select aus einer Design-System-Bibliothek muss einen ref entgegennehmen können, damit Konsument-Anwendungen etwa Fokus setzen, Messungen mit getBoundingClientRect() vornehmen oder mit Animationsbibliotheken interagieren können, die direkten DOM-Zugriff benötigen.

Auch Formular-Bibliotheken wie React Hook Form setzen intern auf forwardRef-kompatible Eingabekomponenten, weil sie Refs nutzen, um unkontrollierte Eingabefelder direkt zu registrieren und deren Werte ohne zusätzliche Re-Renders auszulesen. Eine eigene Eingabekomponente, die keinen ref weiterreicht, lässt sich mit solchen Bibliotheken nicht ohne Weiteres integrieren.

5. Wenn direkter DOM-Zugriff zu viel preisgibt

Reines Weiterreichen des DOM-Elements per forwardRef gibt der aufrufenden Seite vollen Zugriff auf sämtliche native DOM-API-Methoden, etwa focus(), blur(), remove() oder direkte Style-Manipulation. Für einfache Wrapper ist das meist unproblematisch, bei komplexeren, zustandsbehafteten Komponenten kann das aber zu viel Kontrolle nach außen geben und die interne Kapselung aushebeln.

Ein Beispiel ist eine VideoPlayer-Komponente, die intern mehrere DOM-Elemente und React-State koordiniert. Würde man das rohe <video>-Element direkt per Ref freigeben, könnte die aufrufende Seite Methoden wie load() aufrufen, ohne dass der interne React-State der Komponente davon erfährt, was zu Inkonsistenzen zwischen sichtbarem DOM-Zustand und React-State führen kann.

6. useImperativeHandle für eine kontrollierte, eigene Ref-API

useImperativeHandle löst dieses Problem, indem es innerhalb der Komponente definiert, welches Objekt über den weitergereichten ref tatsächlich sichtbar wird, statt automatisch das rohe DOM-Element freizugeben. Man kombiniert dazu einen internen ref auf das native Element mit einem zweiten, öffentlichen ref, der von außen übergeben wird, und definiert explizit ein Objekt mit genau den Methoden, die die aufrufende Seite nutzen darf.

Im folgenden Beispiel erhält die aufrufende Seite über den Ref ausschließlich play() und pause(), beide implementiert, um zusätzlich den internen React-State der Komponente korrekt zu aktualisieren. Direkter Zugriff auf das rohe <video>-Element oder andere DOM-Methoden bleibt verwehrt, wodurch die Komponente ihre interne Konsistenz garantieren kann, während sie trotzdem eine praktische, imperative API nach außen anbietet.


const VideoPlayer = forwardRef(function VideoPlayer(props, ref) {
  const videoRef = useRef(null);
  const [isPlaying, setIsPlaying] = useState(false);

  useImperativeHandle(ref, () => ({
    play() {
      videoRef.current.play();
      setIsPlaying(true);
    },
    pause() {
      videoRef.current.pause();
      setIsPlaying(false);
    },
  }), []);

  return <video ref={videoRef} src={props.src} />;
});

// Nutzung
function App() {
  const playerRef = useRef(null);
  return (
    <>
      <VideoPlayer ref={playerRef} src="/demo.mp4" />
      <button onClick={() => playerRef.current.play()}>Abspielen</button>
    </>
  );
}

7. Mehrere Refs innerhalb einer Komponente koordinieren

Komplexere Komponenten müssen oft mehrere interne Refs koordinieren, etwa ein Container-Element und ein fokussierbares Eingabeelement innerhalb einer zusammengesetzten Komponente wie einem Such-Widget mit Dropdown. In solchen Fällen definiert useImperativeHandle eine öffentliche API, die intern auf mehrere unterschiedliche DOM-Refs zugreift, aber nach außen nur eine einzige, konsistente Schnittstelle zeigt.

Diese Kapselung ist ein wichtiger Designvorteil gegenüber dem einfachen Durchreichen eines einzelnen DOM-Elements: Die interne Struktur der Komponente kann sich ändern, etwa wenn ein zusätzliches Wrapper-Element eingeführt wird, ohne dass die öffentliche Ref-API und damit der Vertrag mit konsumierenden Komponenten sich ändert.

8. Wann man forwardRef besser vermeidet

Nicht jede Komponente braucht forwardRef. Für Komponenten, die ausschließlich innerhalb der eigenen Anwendung genutzt werden und niemals einen DOM-Zugriff von außen benötigen, ist forwardRef unnötige Komplexität. Ebenso ist bei reinen Präsentationskomponenten ohne fokussierbares oder messbares inneres Element ein ref häufig gar nicht sinnvoll nutzbar.

Die Faustregel lautet: Sobald eine Komponente Teil einer wiederverwendbaren Bibliothek ist oder mit Bibliotheken interagieren muss, die selbst auf Refs angewiesen sind, etwa Formular- oder Animationsbibliotheken, ist forwardRef fast immer sinnvoll. Für internen, anwendungsspezifischen Code lohnt sich die Investition oft erst, wenn ein konkreter Anwendungsfall für Fokus- oder Mess-Zugriff von außen entsteht.

9. Zusammenfassender Vergleich der drei Ansätze

Zusammengefasst gibt es drei unterschiedliche Ebenen der Ref-Weiterreichung, jede mit einem anderen Grad an Kontrolle und Kapselung. Native Elemente erhalten Refs automatisch, ohne jeden Eingriff. forwardRef reicht den vollständigen DOM-Zugriff kontrolliert an genau ein inneres Element weiter. useImperativeHandle definiert stattdessen eine bewusst eingeschränkte, eigene API, die nur ausgewählte Methoden nach außen zeigt.

Die folgende Tabelle stellt die drei Ansätze mit ihren jeweiligen Eigenschaften gegenüber, um die Entscheidung für den konkreten Anwendungsfall zu erleichtern.

Ansatz Kontrollgrad Typischer Anwendungsfall Kapselung
Ref auf natives Element voller DOM-Zugriff automatisch einfache HTML-Elemente keine
forwardRef ohne useImperativeHandle voller DOM-Zugriff, gezielt weitergereicht einfache Wrapper-Komponenten gering
forwardRef mit useImperativeHandle nur definierte Methoden sichtbar komplexe, zustandsbehaftete Komponenten hoch
Kein Ref-Support kein externer DOM-Zugriff reine, interne Präsentationskomponenten vollständig

Mironsoft

React-Architektur, Performance und Magento-Frontend-Integration

React-Frontends, die schnell bleiben statt mit jedem Feature langsamer zu werden?

Wir prüfen bestehende React-Anwendungen auf unnötige Re-Renders, aufgeblähte Bundles und fragile State-Verwaltung und bauen daraus ein Frontend, das performant bleibt und sich sauber an Magento oder andere Backends anbindet.

Performance-Audit

Re-Renders, Bundle-Größe und Ladezeiten systematisch messen und beheben.

State-Architektur

Context, Zustand und Server State sauber trennen statt alles in einen Topf zu werfen.

Magento-Integration

GraphQL- oder REST-Anbindung an Magento robust und typsicher aufbauen.

10. Zusammenfassung

forwardRef: Das Wichtigste auf einen Blick

Kernproblem

Refs werden bei Funktionskomponenten standardmäßig nicht an innere Elemente weitergereicht.

Basis-Lösung

forwardRef reicht den ref-Parameter explizit an ein inneres Element weiter.

Feinsteuerung

useImperativeHandle definiert eine eigene, eingeschränkte Ref-API statt vollem DOM-Zugriff.

Typischer Einsatz

Wiederverwendbare Komponentenbibliotheken und Integration mit Formular-/Animationsbibliotheken.

11. FAQ: forwardRef: Das Wichtigste auf einen Blick

1Warum funktioniert ein ref nicht automatisch bei einer selbst geschriebenen Komponente?
Weil React props und ref konzeptionell getrennt behandelt und Funktionskomponenten standardmäßig keinen ref entgegennehmen. Nur native Host-Elemente wie input oder div erhalten Refs automatisch.
2Was genau macht forwardRef?
forwardRef umschließt eine Funktionskomponente und gibt ihr ref als zweiten Parameter neben props, sodass die Komponente selbst entscheiden kann, an welches inneres Element sie den ref weiterreicht.
3Ist forwardRef nach React 19 noch nötig oder gibt es eine neue API?
forwardRef bleibt der etablierte Mechanismus, um ref explizit an eine Funktionskomponente weiterzugeben. Für die meisten Wrapper-Komponenten in Bibliotheken ist es weiterhin der Standardansatz.
4Was ist der Unterschied zwischen forwardRef allein und forwardRef mit useImperativeHandle?
forwardRef allein reicht das native DOM-Element vollständig weiter, inklusive aller DOM-Methoden. useImperativeHandle schränkt das ein und definiert stattdessen ein eigenes Objekt mit nur den Methoden, die die Komponente bewusst nach außen freigeben will.
5Wann sollte ich useImperativeHandle statt einfachem forwardRef verwenden?
Wenn die Komponente internen React-State hat, der bei direktem DOM-Zugriff inkonsistent werden könnte, oder wenn nur eine begrenzte, bewusst gestaltete Menge an Methoden nach außen sichtbar sein soll.
6Warum brauchen Formular-Bibliotheken wie React Hook Form forwardRef-kompatible Komponenten?
Solche Bibliotheken nutzen Refs, um unkontrollierte Eingabefelder direkt zu registrieren und ihre Werte ohne zusätzliche Re-Renders auszulesen. Ohne forwardRef können sie keinen ref auf das innere Eingabeelement erhalten.
7Bricht useImperativeHandle die normale React-Datenflussrichtung?
Es weicht bewusst von der deklarativen, Props-basierten Datenflussrichtung ab, indem es eine imperative API bereitstellt. Das ist gerechtfertigt für Fälle wie Fokus setzen oder Medien abspielen, sollte aber nicht als Ersatz für normalen Props-basierten Datenfluss dienen.
8Braucht jede wiederverwendbare Komponente forwardRef?
Nicht zwingend, nur wenn ein realistischer Bedarf besteht, dass konsumierender Code auf das interne DOM-Element zugreifen muss, etwa für Fokus, Messungen oder Integration mit Ref-basierten Bibliotheken.
9Was passiert, wenn ich versuche, einen ref an eine Komponente ohne forwardRef zu übergeben?
React gibt eine Warnung in der Konsole aus, dass Funktionskomponenten standardmäßig keine Refs entgegennehmen können, und der ref-Wert bleibt null, wodurch jeglicher Zugriff über den Ref fehlschlägt.
10Kann useImperativeHandle mehrere interne DOM-Elemente hinter einer einzigen Ref-API verbergen?
Ja, genau das ist ein wichtiger Vorteil. Eine Komponente kann intern mehrere Refs auf verschiedene DOM-Elemente koordinieren und trotzdem nach außen nur eine einzige, konsistente API über useImperativeHandle anbieten.