State Hoisting: подизање стања и једносмерни ток у Compose-у

Аутор: IT Sectr Објављено: 2026-06-28 Време читања: 8 мин

State Hoisting — је образац у Jetpack Compose-у при којем се стање извлачи из подређене Composable функције у родитељску, а подређена добија податке кроз параметре и обавештава о променама кроз callback-е. Ово је имплементација принципа једносмерног тока података (UDF), при којем се стање подиже нагоре, а догађаји спуштају надоле. Према Google Android Developers, 2026, State Hoisting чини компоненте поновно употребљивим, тестабилним и предвидљивим.

Главно

  • State Hoisting измешта стање из подређеног компонента у родитељски
  • UDF (Unidirectional Data Flow) — стање тече надоле, догађаји нагоре
  • Параметри подређеног компонента: вредност (T) + ламбда (T) -> Unit
  • Поновна употреба — подигнуто стање омогућава коришћење исте функције са различитим изворима
  • Тестирање — State Hoisting поједностављује јединичне тестове, изолујући логику од UI-ја

Шта је State Hoisting у Jetpack Compose-у

State Hoisting (подизање стања) — је образац у којем Composable функција не поседује стање, већ га добија споља. Уместо var унутар функције користе се два параметра: вредност за приказ и ламбда-callback за руковање променама. Технички, ово значи да подређени компонент постаје stateless (нема сопствено стање), а родитељ stateful (поседује стање).

Примјер: TextField компонента из Material3-а не складиште унесени текст унутар себе. Она прима value: String и onValueChange: (String) -> Unit. Родитељ који позива TextField декларише var value by remember { mutableStateOf("") } и прослеђује value и onValueChange. Ово је класични State Hoisting: TextField — једноставна компонента (само приказује и јавља о уносу), родитељ — паметан (поседује стање).

Stateless напрама Stateful: Stateless компонент је лакше тестирати — не зависи од унутрашњег стања, његово понашање је потпуно одређено улазним параметрима. Stateful компонент је згодан за брзо прототипирање, али га је теже поново користити — чврсто је везан за један извор података. State Hoisting даје избор: сваки компонент може бити stateless ако се стање измести нагоре.

Једносмерни ток података (UDF) и State Hoisting

UDF (Unidirectional Data Flow) — је архитектурни принцип при којем се подаци крећу у једном смеру: од извора истине (ViewModel или родитељски Composable) ка UI, а догађаји у супротном смеру. State Hoisting је имплементација UDF-а на нивоу појединачних компонената. Уместо да сваки компонент сам одлучује када и како да промени своје стање, он обавештава родитеља о догађају, а родитељ одлучује како да промени стање.

Предности UDF-а: предвидљивост — стање се мења само на једном месту, што елиминише race conditions; traceability — по стаку позива може се реконструисати ланац промена; тестирање — stateful логика се може издвојити у посебну класу и тестирати без UI-ја. У великим пројектима, UDF у комбинацији са State Hoisting-ом је де факто стандард.

Једини извор истине (Single Source of Truth) — још један принцип који прати UDF. Сваки део стања има тачно један извор. Ако два компонента користе исто стање, извор мора бити заједнички (на нивоу ViewModel-а или заједничког родитеља). State Hoisting гарантује да је извор више у хијерархији и да не долази до дуплирања стања.

СмерШта се преносиКако је имплементирано
Надоле (родитељ → подређени)Вредност за приказПараметар value: T
Нагоре (подређени → родитељ)Догађај о промениПараметар onValueChange: (T) -> Unit

Правила State Hoisting-а: када и како подићи стање

Основно правило: стање треба да буде подигнуто на најмањи могући ниво, довољан за све компоненте којима је потребно. Ако се стање користи само унутар једног компонента — оставите га локалним. Ако два суседна компонента користе исто стање — подигните га у заједничког родитеља. Ако је стање потребно на целом екрану — подигните га у ViewModel.

Правило минималног подизања спречава непотребну комплексност. Нема смисла подизати стање текстуалног поља у ViewModel ако се користи само унутар једног екрана и не чува при поновном креирању Activity-ја. Користите rememberSaveable на нивоу родитеља екрана, а не ViewModel-а, за UI стање које треба да преживи ротацију екрана, али није потребно пословној логици.

Када подизати у ViewModel: ако стање треба да се сачува при поновном креирању Activity-ја, ако је потребно више екрана, ако промена стања покреће пословну логику (мрежне захтјеве, базе података). State Hoisting на нивоу ViewModel-а је типичан образац у MVVM архитектури, где је UI слој stateless, а ViewModel stateful.

kotlin
    // ❌ Лоше: компонент поседује сопствено стање
@Composable
fun BadTextField(label: String) {
    var text by remember { mutableStateOf("") }
    TextField(value = text, onValueChange = { text = it }, label = { Text(label) })
}

    // ✅ Добро: State Hoisting — стање у родитељу
@Composable
fun GoodTextField(value: String, onValueChange: (String) -> Unit, label: String) {
    TextField(value = value, onValueChange = onValueChange, label = { Text(label) })
}

    // Употреба: родитељ поседује стање
@Composable
fun Form() {
    var name by rememberSaveable { mutableStateOf("") }
    GoodTextField(value = name, onValueChange = { name = it }, label = "Name")
}

Примјери State Hoisting-а у реалним компонентама

Размотримо екран пријаве који има два поља (email, лозинка) и дугме. Сва три компонента добијају стање кроз State Hoisting: email и лозинка су под управом родитеља, дугме прима enabled статус као вредност.

kotlin
    // State Hoisting на нивоу екрана
@Composable
fun LoginScreen(viewModel: LoginViewModel) {
    val uiState by viewModel.uiState.collectAsState()

    Column(modifier = Modifier.padding(16.dp)) {
        // Поље Email — State Hoisting путем ламбде
        EmailField(
            email = uiState.email,
            onEmailChange = { viewModel.onEmailChanged(it) }
        )

        // Поље лозинке — слично
        PasswordField(
            password = uiState.password,
            onPasswordChange = { viewModel.onPasswordChanged(it) }
        )

        // Дугме — прима само enabled (само за читање)
        LoginButton(enabled = uiState.isFormValid, onClick = viewModel::login)
    }
}

// Stateless компонент: прима email + callback
@Composable
fun EmailField(email: String, onEmailChange: (String) -> Unit) {
    OutlinedTextField(
        value = email,
        onValueChange = onEmailChange,
        label = { Text("Email") },
        singleLine = true
    )
}

// Stateless компонент дугмета
@Composable
fun LoginButton(enabled: Boolean, onClick: () -> Unit) {
    Button(onClick = onClick, enabled = enabled) {
        Text("Пријава")
    }
}

EmailField и PasswordField — потпуно stateless. Могу се поново користити на било којем екрану, повезани са било којим извором података. LoginButton прима enabled као read-only — ово је још један облик State Hoisting-а где се стање не подиже (дугме не може само да се активира), већ се прослеђује готово. Овакав приступ пружа максималну флексибилност уз минималну повезаност компонената.

State Hoisting напрама локалног стања: критеријуми избора

Није свако стање потребно подизати. Локално стање (State унутар Composable-а) је оправдано када: подаци су потребни само унутар једног компонента, не утичу на суседне елементе, не морају да преживе рекомпозицију одређене секције. На примјер, стање анимације, фокус поља за унос, тренутна позиција скроловања — има смисла држати локално.

Када је State Hoisting неопходан: стање користе више подређених компонената; промена у једном подређеном мора да се одрази у другом; потребно је тестирати логику промене стања одвојено од UI-ја; стање мора да се сачува при поновном креирању Activity-ја. У овим случајевима, локално стање ствара дуплирање и недоследност података.

Хибридни приступ: држите минимално стање локално, остало подижите. Правило Compose-а: «подижите стање онолико колико је потребно и што ниже могуће». У пракси, ово значи почети са локалним remember, и тек када се појави потреба за приступом из другог компонента — подићи ниво. Не радите State Hoisting превентивно — то компликује код без потребе.

Често постављана питања

По чему се State Hoisting разликује од ViewModel-а?

State Hoisting — је образац на нивоу UI компонената. ViewModel — архитектурни слој за пословну логику. State Hoisting може подизати стање на ниво родитељског Composable-а, на ниво екрана или у ViewModel. ViewModel — је највиша тачка подизања за стање које мора да преживи поновно креирање Activity-ја.

Како тестирати компонент са State Hoisting-ом?

Stateless компонент се тестира једноставним прослеђивањем вредности. Позовите Composable са потребним параметрима и проверите приказ путем ComposeTestRule. Промена стања се тестира на нивоу родитеља или ViewModel-а — одвојено од UI-ја. Ово значно поједностављује тестове: није потребно симулирати рекомпозицију унутар компонента.

Може ли се State подићи само за читање?

Да, то је уобичајена пракса. Ако компонент треба само да приказује податке без могућности да их промени — прослеђите State<T> (read-only). Компонент ће бити претплаћен на промене, али неће моћи да их покрене. Ово јача енкапсулацију и штити податке од непожељних мутација.

Шта ако треба да подигнем стање на 3+ нивоа дубине?

За дубоко прослеђивање користите CompositionLocal или прослеђивање кроз параметре родитељског Composable-а. Ако је стање потребно на целом екрану — издвојите га у ViewModel и користите collectAsState(). Прослеђивање кроз 5+ нивоа је знак лоше архитектуре; преиспитајте хијерархију компонената.

Да ли State Hoisting утиче на перформансе?

State Hoisting може благо повећати број рекомпозиција, јер промена у родитељу може да рекомпонује све подређене. Користите derivedStateOf за филтрирање промена и keys у LazyColumn-у за тачкасте ажурирање. У већини сценарија, overhead State Hoisting-а је занемарљив у поређењу са користима од одржаваности.

Резиме

  • State Hoisting измешта стање у родитељски компонент, чинећи подређени stateless
  • UDF гарантује једносмерни ток података: стање надоле, догађаји нагоре
  • Поновна употреба — stateless компоненти се могу повезати са било којим извором података
  • Тестирање — UI тестови проверавају само приказ, логика се тестира одвојено
  • Минимално подизање — подижите тачно онолико колико је потребно
  • ViewModel — највиша тачка подизања за стање са пословном логиком
  • Препорука: почните са локалним remember, подижите само по потреби

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође