Menu

React forwardRef: Refs weitergeben und ref als Prop in 19

Mit forwardRef nimmt eine Komponente eine Ref von ihrem Elternteil an und hängt sie an einen DOM-Knoten darin. In React 19 erhalten Funktionskomponenten ref als normale Prop, also braucht neuer Code kein forwardRef mehr. Sieh beide Versionen in Aktion, dazu useImperativeHandle und TypeScript-Typen.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Mit forwardRef reicht eine Elternkomponente eine ref durch deine Komponente an ein DOM-Element darin, damit das Elternteil focus(), scrollIntoView() und ähnliche Methoden an diesem Element aufrufen kann. In React 19 brauchst du es nicht mehr: Funktionskomponenten erhalten ref als gewöhnliche Prop. forwardRef funktioniert weiterhin, und du siehst es in den meisten bestehenden Codebasen, also zeigt diese Seite beides.

forwardRef nimmt eine Render-Funktion mit zwei Argumenten: props und die ref, die das Elternteil übergeben hat. ref steckt nicht in props; sie kommt separat an. Klicke auf Focus, und die Konsole bestätigt, dass inputRef.current der echte <input>-Knoten ist.

Der Weg in React 19: ref ist eine Prop

Seit React 19 bekommt eine Funktionskomponente ref in ihren Props wie jede andere Prop. Kein Wrapper, kein zweites Argument:

Tippe etwas und klicke auf Log value: Das Elternteil liest den aktuellen Text des Inputs direkt aus dem DOM-Knoten. Lösch ref={ref} am <input> und klicke auf Focus: Die Konsole zeigt einen Fehler, weil inputRef.current jetzt null ist.

forwardRef (alle Versionen)ref-Prop (React 19+)
Wie die Komponente die Ref bekommtZweites Argument, (props, ref)props.ref
Wrapper nötigJaNein
Funktioniert mit useImperativeHandleJaJa
StatusFunktioniert, soll als veraltet markiert werdenDer Weg für neuen Code

Eine Ref, die das Kind ignoriert, bleibt null

ref an eine Komponente zu übergeben bewirkt allein nichts. Die Komponente muss sie auf ein Element setzen. Tut sie das nicht, bleibt ref.current gleich null:

Die Konsole zeigt Forgetful ref: null und Careful ref: INPUT. Das ist der häufigste Grund für „ref.current is null“ an einer eigenen Komponente. Vor React 19 wäre auch Careful null gewesen, wenn es nicht in forwardRef gehüllt war, weil die Ref der Funktion nie übergeben wurde.

useImperativeHandle: eine kleinere API freigeben

Dem Elternteil den ganzen DOM-Knoten zu geben heißt, dass es alles damit machen kann: Styles ändern, Kinder entfernen, Werte lesen, die du privat halten wolltest. Mit useImperativeHandle entscheidest du stattdessen, was ref.current enthält:

Der letzte Button loggt ["focus", "clear"]: Das Elternteil bekommt diese zwei Methoden und sonst nichts, nicht den Input-Knoten. Füge dem Objekt eine Methode select() hinzu und einen Button, der sie aufruft, und das Elternteil bekommt genau eine Fähigkeit mehr.

Das Kind behält seine eigene Ref, inputRef, für das echte Input, und das Handle umhüllt sie. Das dritte Argument ist ein Abhängigkeitsarray wie bei useEffect: Das Handle wird neu gebaut, wenn sich diese Werte ändern.

Nutze das sparsam. Das meiste, was ein Elternteil will (öffnen, schließen, einen Fehler zeigen), ist als Prop besser, etwa isOpen oder error. Imperative Methoden sind für Aktionen ohne natürliche Prop: fokussieren, scrollen, ein Video abspielen, eine Animation starten.

Die Ref auch im Kind nutzen

Manchmal braucht das Kind denselben DOM-Knoten für seine eigene Arbeit, etwa um ihn zu messen oder nach einem Fehler zu fokussieren, während auch das Elternteil eine Ref darauf hält. Ein ref-Attribut kann nur einen Wert annehmen, also führe die beiden mit einem Ref-Callback zusammen:

function AutoGrowTextarea({ ref, ...props }) {
    const localRef = useRef(null);

    function setRefs(node) {
        localRef.current = node;
        if (typeof ref === 'function') ref(node);
        else if (ref) ref.current = node;
    }

    return <textarea ref={setRefs} {...props} />;
}

Die ref des Elternteils kann ein Objekt aus useRef oder eine Funktion sein, also behandle beides. Wenn das Elternteil nur ein paar Aktionen braucht, ist useImperativeHandle oben die sauberere Wahl, weil das Kind den Knoten für sich behält.

Eine Ref durch mehrere Ebenen reichen

Eine Ref reist eine Komponente nach der anderen. Wenn Form ein Field rendert, das ein TextInput rendert, das das <input> rendert, muss jede dieser Komponenten ref an die nächste weitergeben. In React 19 ist das eine Prop mehr zum Weiterreichen (<TextInput ref={ref} />); mit forwardRef musste jede Ebene umhüllt werden. Props zu spreaden ({...props}) trägt sie in älteren Versionen nicht mit, weil ref vor React 19 nie Teil von props war.

Komponenten eines Designsystems sind der übliche Ort, an dem das wichtig ist: Ein Button, Input oder Select, das ein natives Element umhüllt, sollte seine Ref weitergeben, damit die App, die es nutzt, es fokussieren, messen oder an eine Bibliothek übergeben kann, die Popover positioniert.

Von forwardRef migrieren

Die Änderung ist mechanisch: Entferne den Wrapper und lies ref aus den Props.

// Before
const Button = forwardRef(function Button({ variant, ...props }, ref) {
    return <button ref={ref} className={variant} {...props} />;
});

// After (React 19)
function Button({ variant, ref, ...props }) {
    return <button ref={ref} className={variant} {...props} />;
}

Es eilt nicht. forwardRef funktioniert in React 19 weiter, und eine Bibliothek, die React 18 unterstützen muss, muss es behalten, weil React 18 ref nicht als Prop übergibt. Klassenkomponenten sind nicht betroffen: Eine Ref an einer Klassenkomponente zeigt weiterhin auf die Instanz der Komponente.

TypeScript

Bei forwardRef kommen die Typargumente in der Reihenfolge Ref-Typ, dann Props:

import { forwardRef } from 'react';

type FancyInputProps = { label: string };

const FancyInput = forwardRef<HTMLInputElement, FancyInputProps>(
    function FancyInput({ label }, ref) {
        return <input ref={ref} aria-label={label} />;
    }
);

In React 19 typisierst du ref wie jede andere Prop. ComponentProps<'input'> enthält sie bereits:

import { useImperativeHandle, useRef, type ComponentProps, type Ref } from 'react';

function FancyInput(props: ComponentProps<'input'>) {
    return <input {...props} />;
}

type SearchHandle = { focus: () => void; clear: () => void };

function SearchBox({ ref }: { ref?: Ref<SearchHandle> }) {
    const inputRef = useRef<HTMLInputElement>(null);
    useImperativeHandle(ref, () => ({
        focus: () => inputRef.current?.focus(),
        clear: () => {
            if (inputRef.current) inputRef.current.value = '';
        },
    }));
    return <input ref={inputRef} />;
}

// In the parent
const searchRef = useRef<SearchHandle>(null);

Die Seite zu useRef behandelt Refs selbst: DOM-Zugriff, Werte, die ohne Rendern bestehen bleiben, und Ref-Callbacks. Zum Typisieren von Komponenten allgemein siehe React mit TypeScript.

Häufig gestellte Fragen

Was macht forwardRef in React?

Es umhüllt eine Funktionskomponente, damit eine vom Elternteil übergebene ref die Komponente als zweites Argument erreicht, (props, ref). Die Komponente setzt diese Ref dann auf einen DOM-Knoten, damit das Elternteil Methoden wie focus() darauf aufrufen kann.

Ist forwardRef in React 19 veraltet?

Noch nicht, und es funktioniert weiterhin. React 19 übergibt ref an Funktionskomponenten als normale Prop, also braucht neuer Code es nicht, und das React-Team hat angekündigt, forwardRef in einer künftigen Version als veraltet zu markieren.

Warum ist meine Ref an einer eigenen Komponente null?

Die Komponente hat die Ref erhalten, sie aber auf kein Element gesetzt. Hänge sie an einen DOM-Knoten darin: <input ref={ref} />. Vor React 19 wurde die Ref gar nicht übergeben, wenn die Komponente nicht in forwardRef gehüllt war.

Wofür wird useImperativeHandle verwendet?

Es ersetzt, was das Elternteil in ref.current sieht. Statt des ganzen DOM-Knotens gibst du ein Objekt nur mit den Methoden zurück, die du wählst, etwa focus und clear.

Bekommen Klassenkomponenten in React 19 ref als Prop?

Nein. Eine Ref an einer Klassenkomponente zeigt weiterhin auf die Instanz der Komponente. Die Änderung gilt nur für Funktionskomponenten.

Illustration der Programmiersprachen bei Coddy

Lerne mit Coddy zu programmieren

LOS GEHT'S