useRef - Weitere Möglichkeiten
Der React-Hook useRef wird oft ausschließlich mit dem direkten Zugriff auf DOM-Elemente in Verbindung gebracht. Dabei bietet useRef weit mehr Potenzial: Es ermöglicht die Persistenz von veränderlichen Werten über Renderzyklen hinweg, ohne ein erneutes Rendern der Komponente auszulösen. Dadurch eignet sich useRef hervorragend für das Speichern von Instanzvariablen, Timeout-IDs, vorherigen Props oder State-Werten sowie für die Implementierung imperativer Logiken innerhalb funktionaler Komponenten. Diese vielseitigen Anwendungsmöglichkeiten machen useRef zu einem essenziellen Werkzeug für komplexe State- und Lifecycle-Optimierungen in React.
Inhaltsverzeichnis
Einleitung
Zugriff auf DOM-Elemente mit useRef()
ist eine der häufigsten Verwendungsmöglichkeiten. Es gibt allerdings noch einige weitere. In einem Ref-Objekt kann man so gut wie alle Typen von Werten speichern.
Man kann ein Ref-Objekt erstellen und zu jeder Zeit den Wert auslesen oder ändern innerhalb der Komponente, zu welcher dieses Objekt gehört.
Wichtig
Der Wert des Ref-Objekts ändert sich nicht während der Lebenszeit eines Components. Dieser bleibt erhalten, auch wenn ein Component aufgrund von Änderungen neuinitialisiert wird.
Somit kann man useRef()
verwenden, wenn man bestimmte Informationen oder gespeicherte Daten bei Reinitialisierung des Components erhalten möchte.
Referenz und Zustand
Es gibt bestimmte Unterschiede zwischen Referenzen (useRef()
) und Zustandverwaltung (useState()
), auch, wenn beide bestimmte Werte speichern können. Dieser Unterschied ist wichtig, damit man korrekt diese Funktionen und daraus resultierende Objekt sinnvoll und korrekt einsetzt.
Betrachten wir ein Beispiel, bei dem wir ein paar Werte auf unterschiedliche Arten speichern.
In diesem Beispiel definieren wir folgende Variablen:
counterState
: Definiert über State (useState()
)counterRef
: Definiert über Referenzen (useRef()
)counterVariable
: Definiert klassisch als Variable
import { useRef, useState } from 'react';
function UseRefExampleOne() {
const [counterState, setCounterState] = useState(0);
const counterRef = useRef(0);
let counterVariable = 0;
const handleUpdateCounters = () => {
setCounterState(1);
counterRef.current = 1;
counterVariable = 1;
};
return (
<>
<button onClick={handleUpdateCounters}>
Update counters
</button>
<hr />
<ul>
<li>Counter state: {counterState}</li>
<li>Counter ref: {counterRef.current}</li>
<li>Counter variable: {counterVariable}</li>
</ul>
</>
);
}
Beim Klick auf den Button “Update counters” werden nun zwei von drei Variablen aktualisiert.
Die Variable counterVariable
wurde in der Funktion handleUpdateCounters()
in Wirklichkeit aktualisiert. Es gibt jedoch keine Reflektion in UI dafür. Der Grund dafür ist, dass nach Reinitialisierung des Components, ausgelöst durch die State-Änderung, eine neue counterVariable
Variable wurde erzeugt.
Der Wert in counterRef
bleibt hingegen bei 1 bestehen. Eine Neu-Ausführung des Component-Codes ändert der Wert eine Ref-Variable nicht.
- [✅]:
counterState
- [✅]:
counterRef
- [❌]:
counterVariable
Wir verändern nun unser Beispiel und lassen State vollständig weg. Es wird nur mit Refs gearbeitet.
import { useRef } from 'react';
const UseRefExampleTwo = () => {
const counterRefOne = useRef(0);
const counterRefTwo = useRef(0);
let counterVariable = 0;
const handleUpdateCounters = () => {
counterRefOne.current = 1;
counterRefTwo.current = 1;
counterVariable = 1;
console.log('Counter ref one:', counterRefOne.current);
console.log('Counter ref two:', counterRefTwo.current);
console.log('Counter variable:', counterVariable);
};
return (
<>
<button onClick={handleUpdateCounters}>
Update counters
</button>
<hr/>
<ul>
<li>Counter ref one: {counterRefOne.current}</li>
<li>Counter ref two: {counterRefTwo.current}</li>
<li>Counter variable: {counterVariable}</li>
</ul>
</>
);
};
export default UseRefExampleTwo;
Beim Klick nun auf den Button “Update counters” wird sich in der UI nichts verändern, weil keine Aktualisierung durch eine Zustandsänderung ausgelöst wurde.
Wichtig: Änderungen der Ref-Werte lösen keine Reinitialisierung des Components aus.
Referenzen auf Components
Man kann Referenzen nicht nur auf DOM-Elemente setzen. Es ist ebenfalls möglich damit Zugriff auf andere Components zu erhalten.
Lasst uns wieder ein Beispiel anschauen, bestehend aus zwei Parts. Stellen wir uns vor, wir haben ein Formular-Component, welches bestimmte Bestandteile als gesonderte, eigenständige Components einbindet (verwendet). In so einem Fall würden wir vor der Aufgabe stehen, an die Daten aus diesen Components heran zu kommen, wenn wir das Formular absenden und die Daten weiterverarbeiten möchten. Oder, wenn wir diese Elemente nach einem Submit-Event zurücksetzen wollen. Auch hier benötigen wir eine Brücke bzw. eine Lösung.
Der erste Part unseren Beispiels zeigt und die Grundstruktur auf, die uns vor diesem Problem stellt.
import { useState } from 'react';
function CheckboxOptions() {
const [checkboxOne, setCheckboxOne] = useState(false);
const [checkboxTwo, setCheckboxTwo] = useState(false);
function handleUpdateCheckboxOne() {
setCheckboxOne(prevState => !prevState);
}
function handleUpdateCheckboxTwo() {
setCheckboxTwo(prevState => !prevState);
}
return (
<div className="form_field checkbox_wrapper">
<label>
<input
type="checkbox"
id="fieldCheckboxOne"
checked={checkboxOne}
onChange={handleUpdateCheckboxOne}
/>
<span>Checkbox one</span>
</label>
<label>
<input
type="checkbox"
id="fieldCheckboxTwo"
checked={checkboxTwo}
onChange={handleUpdateCheckboxTwo}
/>
<span>Checkbox two</span>
</label>
</div>
);
}
function FormComponent() {
function handleSubmitForm(event) {
event.preventDefault();
}
return (
<form onSubmit={handleSubmitForm}>
<div className="form_field">
<label htmlFor="fieldUsername">Username</label>
<input type="text" id="fieldUsername" name="fieldUsername" />
</div>
<CheckboxOptions />
<div className="form_actions">
<button>Submit</button>
</div>
</form>
);
}
export default FormComponent;
Wie wir an diesem Beispiel sehen können, ist es zunächst nicht ohne Weiteres möglich an die Daten aus dem Component CheckboxOptions
heran zu kommen, um diese in der handleSubmitForm()
Funktion zu verwenden.
An dieser Stelle können Refs hilfreich sein. Man kann mit ihrer Hilfe auf bestimmte Werte oder Funktionen aus anderen Components zugreifen. Sie bilden eine Art Kommunikationsbrücke zwischen zwei Components.
Man kann Referenz auf ein Component mittels Props empfangen. Das Schema würde dabei wie folgt aussehen.
function SideComponent(props) {
...
}
export default SideComponent;
Und im Component mit der Definition der Referenz.
function MainComponent() {
const refSideComponent = useRef(null);
return <SideComponent ref={refSideComponent} />;
}
Diese Verwendungsart funktioniert allerdings erst seit React 19. Vor React 18 müsste man die forwardRef()
Funktion verwenden, als eine Art Wrapper verwenden.
Standardmäßig erhalten Components immer props
als Parameter. Durch die Verwendung der forwardRef()
Funktion erhalten die Components nun zwei, statt einem, Parameter.
Vor React 19 würde das Verwendungsschema wie folgt aussehen.
function SideComponent(props, ref) {}
export default SideComponent;
Und im Component mit der Erzeugung des Referenzobjekts.
function MainComponent() {
const refSideComponet = useRef({});
return <SideComponent ref={refSideComponent} />;
}
Nun bauen wir unser oberes Beispiel so um, dass wir eine Referenz auf das Component CheckboxOptions
erhalten.
import { useState, useRef } from 'react';
function CheckboxOptions(props) {
// Wir entpacken 'ref' aus 'props'
const { ref } = props;
const [checkboxOne, setCheckboxOne] = useState(false);
const [checkboxTwo, setCheckboxTwo] = useState(false);
function handleUpdateCheckboxOne() {
setCheckboxOne(prevState => !prevState);
}
function handleUpdateCheckboxTwo() {
setCheckboxTwo(prevState => !prevState);
}
// Neue Funktion zum Zurücksetzen
function resetCheckboxes() {
setCheckboxOne(false);
setCheckboxTwo(false);
}
// Aufbau des ref-Objekts (initial war leer)
ref.current.resetCheckboxes = resetCheckboxes;
ref.current.selectedCheckboxes = {
checkboxOne: checkboxOne,
checkboxTwo: checkboxTwo
};
return (
<div className="form_field checkbox_wrapper">
<label>
<input
type="checkbox"
id="fieldCheckboxOne"
checked={checkboxOne}
onChange={handleUpdateCheckboxOne}
/>
<span>Checkbox one</span>
</label>
<label>
<input
type="checkbox"
id="fieldCheckboxTwo"
checked={checkboxTwo}
onChange={handleUpdateCheckboxTwo}
/>
<span>Checkbox two</span>
</label>
</div>
);
}
function FormComponent() {
// Ref-Objekt - initial leer
const refCheckboxOptions = useRef({});
function handleSubmitForm(event) {
event.preventDefault();
// Ausgabe von ausgewählten Checkboxen
console.log(refCheckboxOptions.current.selectedCheckboxes);
// Aufruf der Funktion zum Zurücksetzen
refCheckboxOptions.current.resetCheckboxes();
}
return (
<form onSubmit={handleSubmitForm}>
<div className="form_field">
<label htmlFor="fieldUsername">Username</label>
<input type="text" id="fieldUsername" name="fieldUsername" />
</div>
<CheckboxOptions ref={refCheckboxOptions} />
<div className="form_actions">
<button>Submit</button>
</div>
</form>
);
}
export default FormComponent;