Bestehende forwardRef-Komponenten migrieren und in gemischten Codebasen kompatibel bleiben
React 19 hebt einen der aeltesten Sonderfaelle des Frameworks auf: ref laesst sich jetzt direkt als normale Prop entgegennehmen, ganz ohne forwardRef-Wrapper. Dieser Artikel zeigt das neue Muster, wie bestehende forwardRef-Komponenten migriert werden, die TypeScript-Typisierung und wie beide Muster in einer gemischten Codebasis sicher nebeneinander bestehen.
Inhaltsverzeichnis
- 1. Was sich mit React 19 bei refs geaendert hat
- 2. Das neue Muster: ref einfach als Prop entgegennehmen
- 3. Warum forwardRef ueberhaupt noetig war
- 4. Bestehende forwardRef-Komponenten migrieren
- 5. Typisierung in TypeScript
- 6. Abwaertskompatibilitaet in gemischten Codebasen
- 7. Kombination mit useImperativeHandle
- 8. Linting-Fallstricke und ref-Cleanup-Funktionen
- 9. Wann sich die Migration lohnt
- 10. Zusammenfassung
- 11. FAQ
1. Was sich mit React 19 bei refs geaendert hat
Bis einschliesslich React 18 war ref eine Sonderrolle unter den Props: Wer eine Funktionskomponente baute und von aussen per ref auf ein internes DOM-Element oder eine imperative Handle zugreifen wollte, musste die Komponente zwingend mit forwardRef umschliessen. Ein normaler Funktionsaufruf function MyInput(props) { ... } konnte kein ref-Prop entgegennehmen, React entfernte es intern aus den Props, bevor die Funktion ueberhaupt aufgerufen wurde.
React 19 hebt diese Einschraenkung auf: ref wird jetzt wie jeder andere Prop direkt an die Funktionskomponente durchgereicht, ganz ohne forwardRef-Wrapper. Das reduziert Boilerplate spuerbar, vereinfacht die Typisierung in TypeScript und macht Komponenten-Definitionen wieder zu einfachen Funktionen ohne Sonderfall. Fuer neue Komponenten ist forwardRef damit in aller Regel ueberfluessig geworden.
2. Das neue Muster: ref einfach als Prop entgegennehmen
Der Umstieg auf Codeebene ist unspektakulaer: ref wird einfach als weiterer Parameter in der Props-Destrukturierung aufgefuehrt, genau wie jeder andere Wert auch. Innerhalb der Komponente laesst sich der ref dann klassisch per useEffect oder direkt im JSX an ein DOM-Element weiterreichen, exakt wie man es von normalen Props gewohnt ist.
Besonders fuer kleine, wiederverwendbare UI-Bausteine wie ein Eingabefeld oder einen Button lohnt sich dieses vereinfachte Muster sofort, weil der bisherige forwardRef-Wrapper samt der zweiten Callback-Ebene komplett entfaellt. Die Komponente bleibt eine einzige, flache Funktion, was auch das Debuggen im React DevTools-Baum uebersichtlicher macht, da keine zusaetzliche ForwardRef-Ebene mehr im Komponentenbaum auftaucht.
function TextInput({ label, ref, ...props }) {
return (
<label>
{label}
<input ref={ref} {...props} />
</label>
);
}
function Form() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current?.focus();
};
return (
<>
<TextInput label="Name" ref={inputRef} />
<button onClick={focusInput}>Fokussieren</button>
</>
);
}
3. Warum forwardRef ueberhaupt noetig war
Die historische Begruendung fuer forwardRef liegt in Reacts Reconciliation-Modell: ref war von Anfang an kein gewoehnlicher Datenfluss von Eltern- zu Kindkomponente, sondern ein imperativer Seitenkanal, ueber den React direkt eine DOM-Referenz oder eine Instanz zurueckmeldet. Damit dieser Seitenkanal nicht versehentlich als normaler Prop durch die Komponente durchgereicht und dabei veraendert oder ignoriert wird, entfernte React ref konsequent aus dem props-Objekt.
forwardRef gab Komponentenautoren dafuer eine explizite zweite Funktionssignatur, (props, ref) => ..., ueber die der Seitenkanal gezielt wieder zugaenglich gemacht wurde. Das funktionierte zuverlaessig, fuehrte aber dazu, dass jede wiederverwendbare Basiskomponente diesen Wrapper vorsorglich einsetzen musste, selbst wenn zum Zeitpunkt der Erstellung noch gar nicht klar war, ob ref jemals gebraucht wuerde, was in der Praxis zu viel praeventivem Boilerplate fuehrte.
4. Bestehende forwardRef-Komponenten migrieren
Die Migration einer einzelnen Komponente laeuft in drei kleinen Schritten ab: den forwardRef-Aufruf entfernen, die zweite Funktionssignatur (props, ref) zu einer flachen Props-Destrukturierung mit ref als zusaetzlichem Feld zusammenfuehren, und den Komponentennamen als normale function- oder const-Deklaration beibehalten. Der Rueckgabewert und das JSX im Komponentenkoerper bleiben dabei unveraendert, es aendert sich ausschliesslich die aeussere Funktionshuelle.
Fuer groessere Codebasen mit vielen Basiskomponenten empfiehlt sich eine schrittweise Migration Komponente fuer Komponente statt einer grossen Umbau-Aktion, weil forwardRef in React 19 weiterhin funktioniert und beide Muster problemlos nebeneinander existieren koennen. Ein guter Startpunkt sind vielgenutzte, aber einfache Komponenten wie Buttons oder Eingabefelder, bei denen sich der Aufwand pro Migration in wenigen Zeilen erschoepft.
// Vorher: React 18 Muster
const Button = forwardRef(function Button(props, ref) {
return <button ref={ref} className="btn" {...props} />;
});
// Nachher: React 19 Muster
function Button({ ref, ...props }) {
return <button ref={ref} className="btn" {...props} />;
}
5. Typisierung in TypeScript
Unter React 18 musste eine typisierte forwardRef-Komponente den Ref-Typ und den Props-Typ als zwei separate Generics an forwardRef
Das vereinfacht nicht nur die Signatur, sondern macht auch generische, wiederverwendbare Komponenten leichter typisierbar, weil kein separates ForwardRefExoticComponent-Konstrukt mehr um die eigentliche Komponente herum verwaltet werden muss. Bei Komponenten, die mehrere Elementtypen unterstuetzen sollen, etwa polymorphe Buttons, laesst sich der Ref-Typ zusaetzlich ueber ein generisches Typparameter an die Elementart koppeln.
import { Ref } from "react";
interface TextInputProps {
label: string;
ref?: Ref<HTMLInputElement>;
placeholder?: string;
}
function TextInput({ label, ref, placeholder }: TextInputProps) {
return (
<label>
{label}
<input ref={ref} placeholder={placeholder} />
</label>
);
}
6. Abwaertskompatibilitaet in gemischten Codebasen
React 19 markiert forwardRef als veraltet, entfernt es aber nicht; bestehender Code, der forwardRef verwendet, laeuft weiterhin unveraendert. Das ist besonders relevant fuer Projekte, die externe UI-Bibliotheken einbinden, deren eigene Komponenten intern noch forwardRef nutzen, weil diese Bibliotheken selbst erst schrittweise auf das neue React-19-Muster umstellen werden und eine Aktualisierung nicht im eigenen Einflussbereich liegt.
In der Praxis bedeutet das, dass ein und dieselbe Codebasis ueber laengere Zeit beide Muster gleichzeitig enthalten kann, ohne dass daraus funktionale Probleme entstehen: eine mit forwardRef gebaute Komponente laesst sich problemlos innerhalb einer Komponente verwenden, die selbst bereits das neue ref-als-Prop-Muster nutzt, und umgekehrt. Ein Team kann die Migration deshalb entspannt priorisieren, statt unter Zeitdruck alle Komponenten auf einmal umzustellen.
7. Kombination mit useImperativeHandle
Komponenten, die statt eines rohen DOM-Elements eine kuratierte, imperative API nach aussen anbieten wollen, etwa nur eine focus()- und eine reset()-Methode statt des vollen input-Elements, nutzen dafuer weiterhin useImperativeHandle. Der Hook selbst hat sich funktional nicht geaendert, benoetigt aber unter React 19 kein forwardRef mehr als Traegerkomponente, sondern laesst sich direkt in einer normalen Funktionskomponente mit ref-Prop einsetzen.
Das reduziert den Boilerplate fuer imperative Komponenten-APIs auf das Noetigste: Props-Destrukturierung mit ref, ein useRef fuer das interne Element und ein useImperativeHandle-Aufruf, der die gewuenschten Methoden auf dem uebergebenen ref bereitstellt. Fuer komplexere Formularkomponenten mit mehreren imperativen Methoden bleibt dieses Muster dadurch deutlich uebersichtlicher als die zweistufige forwardRef-Variante aus React 18.
function FancyInput({ ref, ...props }) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => inputRef.current?.focus(),
clear: () => {
if (inputRef.current) inputRef.current.value = "";
},
}));
return <input ref={inputRef} {...props} />;
}
8. Linting-Fallstricke und ref-Cleanup-Funktionen
Aeltere ESLint-Konfigurationen mit der react-hooks-Plugin-Regel kennen unter Umstaenden das neue ref-als-Prop-Muster noch nicht und markieren ref faelschlich als unbenutzten oder falsch platzierten Prop; ein Update auf eine aktuelle Plugin-Version behebt das zuverlaessig. Ein weiterer Stolperstein ist die Reihenfolge der Props-Destrukturierung: ref sollte nicht versehentlich Teil eines rest-Spreads werden, der ungefiltert an ein darunterliegendes Element weitergereicht wird, sonst landet ref doppelt im DOM.
React 19 fuehrt ausserdem ref-Cleanup-Funktionen ein: Ein ref-Callback kann jetzt selbst eine Funktion zurueckgeben, die beim Unmount oder bei einer ref-Aenderung automatisch aufgerufen wird, ganz analog zum bekannten useEffect-Cleanup-Muster. In Kombination mit ref als normalem Prop lassen sich dadurch Auf- und Abbau-Logik fuer ein DOM-Element, etwa das Registrieren und Entfernen eines nativen Event-Listeners, direkt und ohne zusaetzlichen useEffect am ref-Callback selbst festmachen.
9. Wann sich die Migration lohnt
Fuer neue Komponenten gibt es praktisch keinen Grund mehr, forwardRef zu verwenden, das neue Muster ist kuerzer, einfacher zu typisieren und funktional identisch. Bei bestehenden Komponenten haengt die Prioritaet davon ab, wie oft sie angefasst werden: Komponenten, die ohnehin gerade ueberarbeitet werden, lassen sich im selben Zug migrieren, waehrend stabile, selten geaenderte Komponenten ruhig mit forwardRef weiterlaufen duerfen.
Ein Laufzeit- oder Bundle-Groessen-Unterschied zwischen beiden Mustern ist praktisch nicht messbar, der Nutzen der Migration liegt ausschliesslich in Lesbarkeit, einfacherer Typisierung und weniger Boilerplate, nicht in Performance. Teams sollten die Migration deshalb als Code-Qualitaets-Massnahme behandeln, die sich gut in ohnehin geplante Refactorings einbetten laesst, statt als isolierte, dringende Aufgabe.
| Aspekt | forwardRef (React 18) | ref als Prop (React 19) | Bewertung |
|---|---|---|---|
| Boilerplate | Zusaetzlicher Wrapper und zweite Signatur | Normale Funktionskomponente | Weniger Code in React 19 |
| TypeScript-Typisierung | forwardRef |
ref direkt im Props-Interface | Einfacher in React 19 |
| Kompatibilitaet | Funktioniert in React 18 und 19 | Nur ab React 19 | forwardRef noetig bei React-18-Support |
| useImperativeHandle | Nur innerhalb forwardRef nutzbar | Direkt in normaler Komponente | Weniger Verschachtelung in React 19 |
| Laufzeitverhalten | Identisch | Identisch | Kein Performance-Unterschied |
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
ref als Prop in React 19: Das Wichtigste auf einen Blick
Kernaenderung
ref wird in React 19 wie jeder andere Prop direkt an Funktionskomponenten durchgereicht, forwardRef ist optional geworden.
Migration
Wrapper entfernen, ref in die Props-Destrukturierung aufnehmen, Komponentenkoerper bleibt unveraendert.
Kompatibilitaet
forwardRef bleibt weiterhin funktionsfaehig, gemischte Codebasen mit beiden Mustern sind problemlos moeglich.
TypeScript
ref laesst sich direkt im Props-Interface typisieren, das umstaendliche forwardRef-Generics-Paar entfaellt.