In React erfüllen sowohl Referenzen (Refs) als auch State zentrale Aufgaben, unterscheiden sich jedoch grundlegend in ihrer Funktionsweise und ihrem Einsatzzweck. Während State hauptsächlich dazu dient, UI-Daten und Komponentenstatus reaktiv zu verwalten und bei Änderungen ein erneutes Rendern auszulösen, ermöglichen Refs den direkten Zugriff auf DOM-Elemente oder Komponenteninstanzen, ohne dabei eine Reaktion auf Änderungen zu triggern. Dieses differenzierte Verhalten macht Refs ideal für imperativen Zugriff, etwa zur Fokussteuerung oder Animation, während State für die deklarative Darstellung und Steuerung von UI-Zuständen zuständig ist. Das Verständnis der Unterschiede und Einsatzszenarien ist essenziell, um React-Anwendungen effizient und performant zu gestalten.

Referenzen auf Elemente können bequem verwendet werden, um auf die DOM-Elemente zuzugreifen und bestimmte Aktionen in diesem Zusammenhang auszuführen. Allerdings sollte man genau prüfen, welche Aktionen dabei ausgeführt (sollen).

Referenzen über useRef() Hook können nützlich sein, um direkt auf ein Input-Element zuzugreifen, um bspw. den Wert auszulesen. Wenn es lediglich um diese einfache Aufgabe geht, können Referenzen tatsächlich die Zustandsverwaltung umgehen.

Sobald man allerdings weiterreichende Aktionen durchführen muss, die auch die UI beeinflussen oder aktualisieren, oder sogar andere Components über eine Änderung benachrichtigen müssen, können Referenzen nicht mehr weiter helfen und State kommt wieder zum Einsatz.

Ich möchte an dieser Stelle das Beispiel aus der Einführung aufgreifen und die Funktionalität ergänzen, bei der nach dem Absenden des Formulars das Input-Element zurückgesetzt wird. Obwohl möglich, sollte man nicht direkt die DOM-Elemente manipulieren.

TypeScript FormWithRef.jsx
import { useState } from 'react';

const FormWithRef = () => {

    const [fieldUsername, setFieldUsername] = useState('');

    const handleUpdateFieldUsername = (event) => {
        setFieldUsername(current => event.target.value);
    };

    const handleSubmitForm = (event) => {
        event.preventDefault();
        console.log(fieldUsername);
        setFieldUsername(current => '');
    };

    return (
        <form onSubmit={handleSubmitForm}>
            <label htmlFor="fieldUsername">Benutzername</label>
            <input
                type="text"
                name="fieldUsername"
                id="fieldUsername"
                value={fieldUsername}
                onChange={handleUpdateFieldUsername}
            />
            <button>Submit</button>
        </form>
    );

};

export default FormWithRef;

In diesem Beispiel haben wir wieder State statt Referenzen im Einsatz. Dadurch werden die Felder, in dem Fall der Wert, über React manipuliert. Entsprechend kriegt auch die UI alle Update korrekt.

React - State für DOM-Manipulation - Zurücksetzen des Input-Feldes

Vergleichs-Tabelle

AspektState (useState)Ref (useRef)
Re-Render bei Änderungjanein
Setter / UpdatesetState(neu)ref.current = neu
Zugriff auf aktuellen Wertstate (lokale Variable)ref.current
Wert ist eingefroren pro Renderja (Snapshot)nein (immer aktuell)
Synchroner Setternein (Update kommt im nächsten Render)ja (sofort sichtbar)
Typischer Use-CaseUI-Werte, Form-Daten, TogglesDOM-Knoten, Timer-IDs, externe Library
In React-Hierarchie sichtbarja (DevTools, Re-Render-Logs)nein (intern, ändert sich „lautlos")

Wann State, wann Ref?

State, wenn:

  • Der Wert in JSX gerendert wird ({count}, {user.name}).
  • Eine Änderung soll die UI aktualisieren.
  • Andere Komponenten (Kinder) sollen den Wert via Props sehen.

Ref, wenn:

  • Der Wert ist ein DOM-Knoten (focus(), scrollTo()).
  • Eine Timer-ID oder Subscription muss zwischen Renders überleben.
  • Ein vorheriger Wert muss gemerkt werden (klassisches usePrevious).
  • Eine mutable Instanz einer externen Library lebt in der Komponente.

Klassische usePrevious-Pattern

Ein typischer Ref-Use-Case ohne DOM: den vorherigen Wert merken.

TypeScript usePrevious.jsx
import { useRef, useEffect } from 'react';

function usePrevious(value) {
    const ref = useRef();
    useEffect(() => {
        ref.current = value;
    });
    return ref.current;
}

function Counter({ count }) {
    const prevCount = usePrevious(count);
    return <p>Jetzt: {count}, vorher: {prevCount}</p>;
}

State würde nicht passen — das Speichern des Vorher-Werts darf kein Re-Render auslösen.

Häufige Stolperfallen

Ref-Wert in JSX rendern — funktioniert beim ersten Render NICHT.

<p>{ref.current}</p> ist gefährlich: bei Mount ist current null (DOM noch nicht da), bei späteren Renders zeigt das Element-Render den AKTUELLEN current — aber kein Re-Render bei Änderung. Wer Wert anzeigen will: State.

State im Render-Body lesen ist eingefroren, ref.current immer aktuell.

Closure-Falle: setTimeout(() => alert(count), 1000) zeigt den count vom Render-Zeitpunkt. setTimeout(() => alert(ref.current), 1000) zeigt den aktuellen Wert beim Timer-Feuer. Daher: Refs als „Spion" hinter dem aktuellen Stand.

Refs sind synchron, State ist asynchron.

ref.current = 5; console.log(ref.current) ergibt sofort 5. setState(5); console.log(state) ergibt den ALTEN Wert — Re-Render kommt erst danach. Wer „sofortigen" Wert braucht: Ref. Aber kein UI-Update.

Refs für Form-Felder = uncontrolled — kein Re-Render pro Tastendruck.

Bei großen Formularen messbar schneller als state-controlled. Trade-off: Live-Validation oder Conditional-Disable-Logic braucht Extra-State.

State NIE mit ref.current verwechseln — eine Ref zu rendern ist Code-Smell.

const { current } = ref; return <div>{current}</div> — funktioniert manchmal, ist aber fragil. State macht den Update-Mechanismus explizit.

Refs in Dependency-Arrays: nicht nötig (sind stabil).

const ref = useRef(0); useEffect(() => ..., [ref]) ist redundant — ref-Objekt selbst ist stabil zwischen Renders. ESLint-Plugin weiß das.

useState mit Funktion vs. `useRef` bei stabilen IDs.

Beide Patterns gehen: useState(() => crypto.randomUUID())[0] oder useRef(crypto.randomUUID()).current. State ist semantisch klarer („dieser Wert gehört zur Komponente"), Ref hat keinen Setter-Overhead.

State-Setter ist stabil, Ref ist ein neues Object pro Komponente.

Beide sind stabil im Sinne von „bleiben über Renders". Aber ref aus useRef ist pro Komponenten-Instanz EIN Objekt (verschiedene Instanzen haben verschiedene Refs). State-Setter ist ebenfalls pro Instanz.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Refs

Zur Übersicht