State Hoisting ist ein Muster in Jetpack Compose, bei dem der Zustand aus einer untergeordneten Composable-Funktion in die übergeordnete verschoben wird und die untergeordnete Funktion Daten über Parameter erhält und Änderungen über Callbacks meldet. Dies ist eine Implementierung des Prinzips des unidirektionalen Datenflusses (UDF), bei dem der Zustand nach oben gehoben und Ereignisse nach unten geleitet werden. Laut Google Android Developers, 2026 macht State Hoisting Komponenten wiederverwendbar, testbar und vorhersagbar.
Das Wichtigste
State Hoisting ist ein Muster, bei dem eine Composable-Funktion keinen Zustand besitzt, sondern ihn von außen erhält. Anstelle von var innerhalb der Funktion werden zwei Parameter verwendet: ein Wert zur Anzeige und ein Lambda-Callback zur Behandlung von Änderungen. Technisch bedeutet dies, dass die untergeordnete Komponente zustandslos (stateless) wird, während die übergeordnete zustandsbehaftet (stateful) ist.
Beispiel: Die TextField-Komponente aus Material3 speichert den eingegebenen Text nicht intern. Sie akzeptiert value: String und onValueChange: (String) -> Unit. Der übergeordnete Aufrufer von TextField deklariert var value by remember { mutableStateOf("") } und übergibt value und onValueChange. Dies ist klassisches State Hoisting: TextField ist eine dumme Komponente (zeigt nur an und meldet Eingaben), der Eltern ist intelligent (besitzt den Zustand).
Stateless vs Stateful: Eine stateless Komponente ist einfacher zu testen — sie hängt nicht von internem Zustand ab, ihr Verhalten wird vollständig durch Eingabeparameter bestimmt. Eine stateful Komponente ist für schnelles Prototyping praktisch, aber schwerer wiederverwendbar: Sie ist stark an eine einzelne Datenquelle gebunden. State Hoisting gibt Ihnen die Wahl: Jede Komponente kann durch Verschieben des Zustands nach oben stateless gemacht werden.
UDF (Unidirektionaler Datenfluss) ist ein Architekturprinzip, bei dem Daten in eine Richtung fließen: von der Quelle der Wahrheit (ViewModel oder übergeordneter Composable) zur UI, und Ereignisse in die entgegengesetzte Richtung. State Hoisting ist die Implementierung von UDF auf der Ebene einzelner Komponenten. Anstatt dass jede Komponente selbst entscheidet, wann und wie sie ihren Zustand ändert, benachrichtigt sie die übergeordnete Komponente über ein Ereignis, und die übergeordnete entscheidet, wie der Zustand zu ändern ist.
Vorteile von UDF: Vorhersagbarkeit — Zustand ändert sich nur an einer Stelle, wodurch Wettlaufsituationen ausgeschlossen werden; Rückverfolgbarkeit — der Aufrufstapel ermöglicht die Rekonstruktion der Änderungskette; Testen — stateful Logik kann in eine separate Klasse extrahiert und ohne UI getestet werden. In großen Projekten ist UDF in Kombination mit State Hoisting der De-facto-Standard.
Single Source of Truth ist ein weiteres Prinzip, das UDF begleitet. Jeder Zustandsteil hat genau eine Quelle. Wenn zwei Komponenten denselben Zustand verwenden, sollte die Quelle gemeinsam genutzt werden (auf ViewModel- oder gemeinsamer Eltern-Ebene). State Hoisting stellt sicher, dass die Quelle in der Hierarchie oben liegt und keine Zustandsverdopplung auftritt.
| Richtung | Was wird übergeben | Wie wird es implementiert |
|---|---|---|
| Nach unten (Eltern → Kind) | Anzuzeigender Wert | Parameter value: T |
| Nach oben (Kind → Eltern) | Änderungsereignis | Parameter onValueChange: (T) -> Unit |
Die Hauptregel: Der Zustand sollte auf die minimal mögliche Ebene angehoben werden, die für alle Komponenten ausreicht, die ihn benötigen. Wenn der Zustand nur innerhalb einer Komponente verwendet wird — belassen Sie ihn lokal. Wenn zwei benachbarte Komponenten denselben Zustand benötigen — heben Sie ihn auf den gemeinsamen Eltern an. Wenn der Zustand auf dem gesamten Bildschirm benötigt wird — heben Sie ihn auf die ViewModel an.
Die Regel der minimalen Anhebung verhindert unnötige Komplexität. Es macht keinen Sinn, den Zustand eines Textfelds in eine ViewModel anzuheben, wenn er nur innerhalb eines Bildschirms verwendet wird und beim Neuerstellen der Activity nicht beibehalten wird. Verwenden Sie rememberSaveable auf der Ebene des Bildschirm-Elterns, nicht auf ViewModel, für UI-Zustand, der eine Bildschirmdrehung überleben soll, aber von der Geschäftslogik nicht benötigt wird.
Wann in ViewModel anheben: wenn der Zustand die Neuerstellung der Activity überleben muss, wenn er von mehreren Bildschirmen benötigt wird, wenn die Zustandsänderung Geschäftslogik auslöst (Netzwerkanfragen, Datenbank). State Hoisting auf ViewModel-Ebene ist ein Standardmuster in der MVVM-Architektur, bei der die UI-Schicht stateless und die ViewModel stateful ist.
// ❌ Schlecht: Komponente besitzt eigenen Zustand
@Composable
fun BadTextField(label: String) {
var text by remember { mutableStateOf("") }
TextField(value = text, onValueChange = { text = it }, label = { Text(label) })
}
// ✅ Gut: State Hoisting — Zustand im Eltern
@Composable
fun GoodTextField(value: String, onValueChange: (String) -> Unit, label: String) {
TextField(value = value, onValueChange = onValueChange, label = { Text(label) })
}
// Verwendung: Eltern besitzt Zustand
@Composable
fun Form() {
var name by rememberSaveable { mutableStateOf("") }
GoodTextField(value = name, onValueChange = { name = it }, label = "Name")
}
Betrachten Sie einen Login-Bildschirm mit zwei Feldern (E-Mail, Passwort) und einem Button. Alle drei Komponenten erhalten den Zustand über State Hoisting: E-Mail und Passwort werden vom Eltern verwaltet, der Button erhält seinen enabled-Status als Wert.
// State Hoisting auf Bildschirmebene
@Composable
fun LoginScreen(viewModel: LoginViewModel) {
val uiState by viewModel.uiState.collectAsState()
Column(modifier = Modifier.padding(16.dp)) {
// E-Mail-Feld — State Hoisting via Lambda
EmailField(
email = uiState.email,
onEmailChange = { viewModel.onEmailChanged(it) }
)
// Passwort-Feld — ebenso
PasswordField(
password = uiState.password,
onPasswordChange = { viewModel.onPasswordChanged(it) }
)
// Button — erhält nur enabled (schreibgeschützt)
LoginButton(enabled = uiState.isFormValid, onClick = viewModel::login)
}
}
// Stateless-Komponente: empfängt E-Mail + Callback
@Composable
fun EmailField(email: String, onEmailChange: (String) -> Unit) {
OutlinedTextField(
value = email,
onValueChange = onEmailChange,
label = { Text("E-Mail") },
singleLine = true
)
}
// Stateless-Button-Komponente
@Composable
fun LoginButton(enabled: Boolean, onClick: () -> Unit) {
Button(onClick = onClick, enabled = enabled) {
Text("Anmelden")
}
}
EmailField und PasswordField sind vollständig zustandslos. Sie können auf jedem Bildschirm wiederverwendet werden, indem sie an jede beliebige Datenquelle angeschlossen werden. LoginButton erhält enabled als schreibgeschützt — dies ist eine weitere Form von State Hoisting, bei der der Zustand nicht angehoben wird (der Button kann sich nicht selbst aktivieren), sondern fertig nach unten gereicht wird. Dieser Ansatz bietet maximale Flexibilität bei minimaler Kopplung der Komponenten.
Nicht jeder Zustand muss angehoben werden. Lokaler Zustand (State innerhalb eines Composable) ist gerechtfertigt, wenn: Daten nur innerhalb einer Komponente benötigt werden, keine benachbarten Elemente beeinflusst werden und der Zustand die Neuzusammensetzung eines bestimmten Abschnitts nicht überleben muss. Zum Beispiel Animationszustand, Fokus eines Eingabefelds, aktuelle Scrollposition — es ist sinnvoll, diese lokal zu halten.
Wann State Hoisting notwendig ist: Der Zustand wird von mehreren untergeordneten Komponenten verwendet; eine Änderung in einer untergeordneten soll sich in einer anderen widerspiegeln; die Logik der Zustandsänderungen muss getrennt von der UI getestet werden; der Zustand soll die Neuerstellung der Activity überleben. In diesen Fällen erzeugt lokaler Zustand Duplikate und Dateninkonsistenz.
Hybrider Ansatz: Halten Sie minimalen Zustand lokal, heben Sie den Rest an. Die Compose-Regel: „Heben Sie Zustand so hoch wie nötig und so niedrig wie möglich an.“ In der Praxis bedeutet dies, mit lokalem remember zu beginnen und die Ebene nur dann anzuheben, wenn der Zugriff von einer anderen Komponente benötigt wird. Wenden Sie State Hoisting nicht präventiv an — es verkompliziert den Code ohne Notwendigkeit.
Häufig gestellte Fragen
State Hoisting ist ein Muster auf UI-Komponentenebene. ViewModel ist eine architektonische Schicht für Geschäftslogik. State Hoisting kann Zustand auf die Ebene des übergeordneten Composable, auf Bildschirmebene oder in die ViewModel anheben. ViewModel ist der höchste Anhebungspunkt für Zustand, der die Neuerstellung der Activity überleben muss.
Eine zustandslose Komponente wird durch einfaches Übergeben von Werten getestet. Rufen Sie das Composable mit den erforderlichen Parametern auf und überprüfen Sie die Anzeige mit ComposeTestRule. Zustandsänderungen werden auf Eltern- oder ViewModel-Ebene getestet — getrennt von der UI. Dies vereinfacht Tests erheblich: Es ist nicht nötig, die Neuzusammensetzung innerhalb der Komponente zu simulieren.
Ja, das ist gängige Praxis. Wenn eine Komponente Daten nur anzeigen muss, ohne sie ändern zu können — übergeben Sie State<T> (schreibgeschützt). Die Komponente wird Änderungen abonnieren, kann sie aber nicht auslösen. Dies stärkt die Kapselung und schützt Daten vor unerwünschten Mutationen.
Für tiefe Übergabe verwenden Sie CompositionLocal oder übergeben Sie über Parameter des übergeordneten Composable. Wenn Zustand auf dem gesamten Bildschirm benötigt wird — extrahieren Sie ihn in eine ViewModel und verwenden Sie collectAsState(). Die Übergabe über 5+ Ebenen ist ein Zeichen falscher Architektur; überdenken Sie die Komponentenhierarchie.
State Hoisting kann die Anzahl der Neuzusammensetzungen geringfügig erhöhen, da eine Änderung im Eltern alle Kinder neu zusammensetzen kann. Verwenden Sie derivedStateOf zum Filtern von Änderungen und keys in LazyColumn für gezielte Aktualisierungen. In den meisten Szenarien ist der Overhead von State Hoisting im Vergleich zum Vorteil der Wartbarkeit vernachlässigbar.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch