Das gezielte Verwalten von Zuständen ist eine der zentralen Herausforderungen in der Entwicklung mit React. Wenn mehrere Komponenten denselben Datenstand benötigen oder verändern sollen, stößt man schnell an die Grenzen lokaler Zustände. Hier setzt das Konzept des Lifting State an: ein Prinzip, das hilft, Zustände auf eine übergeordnete Ebene zu heben und so eine konsistente Datenbasis für mehrere Komponenten zu schaffen. Dieses Vorgehen verbessert nicht nur die Struktur und Wartbarkeit des Codes, sondern fördert auch ein klareres Verständnis der Datenflüsse innerhalb einer Anwendung.
Problem
Es entsteht nicht selten die Notwendigkeit, ein Component A über die Änderung des Zustandes im Component B zu informieren.
Um das Problem besser beschreiben zu können, definieren wir zwei Components.
Component 1: SearchBar
import { useState } from 'react';
const SearchBar = () => {
const [searchTerm, setSearchTerm] = useState('');
const handleUpdateSearchTerm = (event) => {
setSearchTerm(currentSearchTerm => event.target.value);
};
return <input type="search" onChange={handleUpdateSearchTerm} />;
};
export default SearchBar;Component 2: SearchOverview
const SearchOverview = () => {
return <p>Aktuelle Suchanfrage: {searchTerm}</p>
};
export default SearchOverview;Zusammenführung (Verwendung) in App Component.
import SearchBar from './components/SearchBar';
import SearchOverview from './components/SearchOverview';
class App extends React.Component {
render() {
return (
<>
<h1>State Sharing</h1>
<hr/>
<SearchBar />
<SearchOverview />
</>
);
}
}
export default App;Wir haben hier das Problem, dass die Zustandsverwaltung im SearchBar Component definiert ist. Aktuell weiß das Component SearchOverview, in welchem wir ausgeben möchten, was in SearchBar eingegeben wurde, nichts über irgendwelche Zustände im Component SearchBar. Es besteht einfach keine Verbindung.
Man könnte sicherlich das Problem lösen, indem man beide Components zu einem Component zusammenführt. Dies ist allerdings nicht immer möglich bzw. manchmal gar nicht gewünscht, da man versuchen sollte die Component-Landschaft so granular wie möglich zu halten.
Lösung
Dieses Problem kann mit dem sogenannten State Lifting gelöst werden. Wenn State Lifting (das Anheben der Zustandsverwaltung) eingesetzt wird, wird der Zustand in keinem von den Beiden Components (in diesem Fall SearchBar und SearchOverview) verwaltet, sondern eine Ebene höher angesetzt.
Die Verwaltung des Zustandes verlagert sich zum Eltern-Component. Um genauer zu sein, zum nächstgelegenen Eltern-Component. Um das bildlich sich vorstellen zu können, schauen wir uns die folgende Grafik an.

In unserem Beispiel haben SearchBar und SearchOverview einen gemeinsamen Eltern-Component, nämlich App. Warum? Weil wir sie dort einbinden. Würden wir sie in einem anderen Component, so wäre das andere Component eben das Parent-Component. In React sind Components in einer Art Baumstruktur aufgebaut, die bis zum Root-Component (in der Regel App) geht.
Die anderen Components in der Grafik sind lediglich beispielhaft da. Wenn z.B. wir die Zustandsverwaltung zwischen unterschiedlichen Benutzer-Typen aufteilen müssten, wäre unser verwaltendes Component (oder das nächstgelegene Eltern-Component) das Users Component.
Die geteilte Zustandsverwaltung wird durch den Einsatz von Props und das Auslagern in das Eltern-Component realisiert. Sie wird "angehoben", was der Begriff State Lifting beschreibt.
Lösung - Beispiel Umbau
Nun bauen das oben gezeigte Beispiel so um, dass die Zustände zwischen den beiden Components ausgetauscht werden können.
Aus unserem SearchBar Component entfernen wir die Zustandsverwaltung und platzieren eine Props-Eigenschaft onUpdateSearch.
const SearchBar = ({ onUpdateSearch }) => {
return <input type="search" onChange={onUpdateSearch} />;
};
export default SearchBar;Im Component SearchOverview ergänzen wir die Prop-Eigenschaft searchTerm.
const SearchOverview = ({ currentSearchTerm }) => {
return <p>Aktuelle Suchanfrage: {currentSearchTerm}</p>
};
export default SearchOverview;Unser App Component hat nun die Zustandsverwaltung übernommen.
import SearchBar from './components/SearchBar';
import SearchOverview from './components/SearchOverview';
const App = () => {
const [searchTerm, setSearchTerm] = useState('');
const handleUpdateSearchTerm = (event) => {
setSearchTerm(s => event.target.value);
};
return (
<>
<SearchBar onUpdateSearch={handleUpdateSearchTerm} />
<SearchOverview currentSearchTerm={searchTerm} />
</>
);
};
export default App;Mit diesem Umbau haben wir den Zustand erreicht, dass wir im Component SearchOverview nun über die Änderungen, in diesem Fall Input-Event, im Component SearchBar über die Brücke, also das Eltern-Component App, informiert werden.
Weil React nicht nur das Component, in dem die Zustandsverwaltung stattfindet, bei Änderung von überwachten Einheiten neurendert, sondern auch die Kind-Elemente, die Zustandswerte einsetzen/verwenden, werden sie ebenfalls von React aktualisiert.

Props und Schema
An dieser Stelle möchte ich etwas genauer auf die Props eingehen und das Schema etwas mehr erklären.
Im Component App haben wir die Funktion handleUpdateSearchTerm definiert. Diese aktualisiert einfach unseren Zustandswert für das Suchwort. Soweit ok. Nichts besonderes.
Diese Funktion schnappen wir und übergeben sie in das Component SearchBar. Dabei binden wir die Referenz auf diese Funktion an den Namen onUpdateSearch. Das könnte jeder anderer Name sein. Definiert wird dieser im Ziel-Component, in diesem Fall in SearchBar als Prop-Eigenschaftsname.
Was bedeutet es? Es bedeutet, dass wir in unserem SearchBar Component eigentlich eine Referenz auf die handleUpdateSearchTerm Funktion haben. Entsprechend wird auch tatsächlich diese Funktion aufgerufen, wenn der registrierte Event getriggert wird.
Ok, lasst uns das Ganze anhand eines Beispiels verdeutlichen. Wir erstellen dafür möglichst einfache Components. Diese packe ich in eine Datei, sodass wir uns die Importe sparen können.
const ChildComp = ({ refFunction }) => {
return (
<button onClick={refFunction}>
Call function
</button>
);
};
const ParentComp = () => {
const originalFunc = (event) => {
console.log('Button in ChildComp clicked');
};
return <ChildComp refFunction={originalFunc} />
};
export default ParentComp;Was haben wir hier? Wir haben hier ein ParentComp, indem wir eine Funktion originalFunc definieren. Diese Funktion macht in diesem Beispiel nichts. Uns reicht aus, dass wir ein Signal bekommen, dass genau diese Funktion aufgerufen wurde. Dazu dient unsere console.log Anweisung.
In unserem ChildComp definieren wird eine Prop-Eigenschaft namens refFunction. Das ist ein Name, an den wir im ParentComp die Funktion originalFunc binden.
Zusätzlich haben wir im ChildComp einen Button erstellt und diesem einen Klick-Event angehängt. Jedes Mal, wenn der Klick-Event durch einen Klick abgefeuert wird, wird die Funktion refFunction aufgerufen. Aber in Wirklichkeit, weil wir die originalFunc als Prop hier hineingegeben haben (als Referenz), wird die originalFunc aufgerufen. Das können wir anhand dem Log in der Konsole im Browser sehen.

Besonderheiten
State Lifting ist die Lösung für 'wo gehört der State hin?'.
Wenn zwei Geschwister-Komponenten denselben Wert brauchen, wandert der State in den GEMEINSAMEN Eltern. Eines der Kinder bekommt den Wert als Prop, das andere bekommt den Setter als Callback-Prop. Klassischer Top-Down-Datenfluss.
Single Source of Truth — Daten nur an EINER Stelle.
Daten sollten an genau einer Stelle in der Komponenten-Hierarchie leben. Alle anderen Stellen lesen sie als Prop. Doppelter State zwischen Eltern und Kind führt fast immer zu Sync-Bugs.
Lifting bedeutet höheres Re-Render-Risiko.
Wenn State in einer hohen Komponente liegt, rendern alle Kinder bei jeder Änderung neu — auch die, die den Wert nicht nutzen. Bei Performance-Problemen: React.memo auf den Kindern, die den State nicht brauchen, oder State weiter unten in einer kleineren Sub-Komponente halten.
Callback-Prop mit on... Namenskonvention.
Der Setter wandert als Prop nach unten — typischerweise mit Namen wie onChange, onSubmit, onSelect. Im Kind heißt der Handler oft handle...: const handleChange = (v) => props.onChange(v).
Bei Lifting über mehrere Ebenen: Context API oder State-Management-Library.
Wenn der State 4-5 Ebenen tief durchgereicht werden muss (Prop-Drilling), ist Context oder Redux/Zustand die bessere Wahl. Faustregel: bis 2-3 Ebenen ist Lifting okay, darüber wird's lästig.
useCallback für stabile Setter-Referenzen bei React.memo-Kindern.
const handleChange = useCallback((v) => setX(v), []) hält die Funktion stabil zwischen Renders. Wichtig, wenn das Kind mit React.memo optimiert ist — sonst rendert es bei jedem Eltern-Render trotzdem neu, weil die Setter-Referenz sich ändert.
Lifting funktioniert auch mit useReducer.
Bei komplexem State: useReducer in den Eltern, state und dispatch als Props nach unten geben. Die Action-Definitionen bleiben zentral, die Kinder können dispatchen, ohne die Reducer-Logik zu kennen.
Lifting ist EINE Form der Komposition, nicht die einzige.
Manche Daten gehören NICHT in eine Eltern-Komponente. Beispiel: Modal-Open-State gehört in das Modal, nicht in die App. Wenn nur eine Komponente den State braucht — local halten. Lifting ist nur nötig, wenn MEHRERE Komponenten zugreifen.
Weiterführende Ressourcen
Externe Quellen
- Sharing State Between Components – react.dev
- Thinking in React – react.dev
- Passing Data Deeply with Context – react.dev