State Hoisting — adalah pola di Jetpack Compose di mana state dikeluarkan dari fungsi Composable anak ke fungsi induk, dan anak menerima data melalui parameter dan memberi tahu perubahan melalui callback. Ini adalah implementasi dari prinsip aliran data searah (UDF), di mana state diangkat ke atas dan event turun ke bawah. Menurut Google Android Developers, 2026, State Hoisting membuat komponen dapat digunakan kembali, dapat diuji, dan dapat diprediksi.
Poin Utama
State Hoisting (mengangkat state) — adalah pola di mana fungsi Composable tidak memiliki state, tetapi menerimanya dari luar. Alih-alih var di dalam fungsi, digunakan dua parameter: nilai untuk ditampilkan dan lambda-callback untuk menangani perubahan. Secara teknis, ini berarti komponen anak menjadi stateless (tidak memiliki state sendiri), dan induk menjadi stateful (memiliki state).
Contoh: komponen TextField dari Material3 tidak menyimpan teks yang dimasukkan di dalamnya. Ia menerima value: String dan onValueChange: (String) -> Unit. Induk yang memanggil TextField mendeklarasikan var value by remember { mutableStateOf("") } dan meneruskan value serta onValueChange. Ini adalah State Hoisting klasik: TextField — komponen bodoh (hanya menampilkan dan melaporkan input), induk — pintar (memiliki state).
Stateless vs Stateful: Komponen Stateless lebih mudah diuji — ia tidak bergantung pada state internal, perilakunya sepenuhnya ditentukan oleh parameter input. Komponen Stateful nyaman untuk pembuatan prototipe cepat, tetapi lebih sulit digunakan kembali — ia terikat erat pada satu sumber data. State Hoisting memberikan pilihan: komponen apa pun dapat dibuat stateless dengan memindahkan state ke atas.
UDF (Unidirectional Data Flow) — adalah prinsip arsitektur di mana data bergerak dalam satu arah: dari sumber kebenaran (ViewModel atau induk Composable) ke UI, dan event ke arah sebaliknya. State Hoisting adalah implementasi UDF di tingkat komponen individual. Alih-alih setiap komponen memutuskan sendiri kapan dan bagaimana mengubah state-nya, ia melaporkan event ke induk, dan induk memutuskan bagaimana mengubah state.
Keuntungan UDF: prediktabilitas — state hanya berubah di satu tempat, yang menghilangkan race conditions; traceability — dari tumpukan panggilan dapat direkonstruksi rantai perubahan; pengujian — logika stateful dapat dipisahkan ke kelas terpisah dan diuji tanpa UI. Dalam proyek besar, UDF yang dikombinasikan dengan State Hoisting adalah standar de facto.
Sumber Kebenaran Tunggal (Single Source of Truth) — prinsip lain yang menyertai UDF. Setiap bagian state memiliki tepat satu sumber. Jika dua komponen menggunakan state yang sama, sumber harus bersama (di tingkat ViewModel atau induk bersama). State Hoisting menjamin bahwa sumber berada lebih tinggi dalam hierarki dan tidak terjadi duplikasi state.
| Arah | Apa yang dikirim | Bagaimana diimplementasikan |
|---|---|---|
| Ke bawah (induk → anak) | Nilai untuk ditampilkan | Parameter value: T |
| Ke atas (anak → induk) | Event perubahan | Parameter onValueChange: (T) -> Unit |
Aturan utama: state harus diangkat ke level serendah mungkin yang cukup untuk semua komponen yang membutuhkannya. Jika state hanya digunakan di dalam satu komponen — biarkan lokal. Jika dua komponen tetangga membutuhkan state yang sama — angkat ke induk bersama. Jika state diperlukan di seluruh layar — angkat ke ViewModel.
Aturan pengangkatan minimal mencegah kompleksitas yang tidak perlu. Tidak ada gunanya mengangkat state bidang teks ke ViewModel jika hanya digunakan di dalam satu layar dan tidak disimpan saat Activity dibuat ulang. Gunakan rememberSaveable di tingkat induk layar, bukan ViewModel, untuk state UI yang harus bertahan dari rotasi layar tetapi tidak diperlukan oleh logika bisnis.
Kapan mengangkat ke ViewModel: jika state harus dipertahankan saat Activity dibuat ulang, jika diperlukan oleh beberapa layar, jika perubahan state memicu logika bisnis (permintaan jaringan, database). State Hoisting di tingkat ViewModel adalah pola tipikal dalam arsitektur MVVM, di mana lapisan UI bersifat stateless dan ViewModel bersifat stateful.
// ❌ Buruk: komponen memiliki state sendiri
@Composable
fun BadTextField(label: String) {
var text by remember { mutableStateOf("") }
TextField(value = text, onValueChange = { text = it }, label = { Text(label) })
}
// ✅ Baik: State Hoisting — state di induk
@Composable
fun GoodTextField(value: String, onValueChange: (String) -> Unit, label: String) {
TextField(value = value, onValueChange = onValueChange, label = { Text(label) })
}
// Penggunaan: induk memiliki state
@Composable
fun Form() {
var name by rememberSaveable { mutableStateOf("") }
GoodTextField(value = name, onValueChange = { name = it }, label = "Name")
}
Pertimbangkan layar login yang memiliki dua bidang (email, kata sandi) dan sebuah tombol. Ketiga komponen menerima state melalui State Hoisting: email dan kata sandi dikelola oleh induk, tombol menerima status enabled sebagai nilai.
// State Hoisting di tingkat layar
@Composable
fun LoginScreen(viewModel: LoginViewModel) {
val uiState by viewModel.uiState.collectAsState()
Column(modifier = Modifier.padding(16.dp)) {
// Bidang Email — State Hoisting melalui lambda
EmailField(
email = uiState.email,
onEmailChange = { viewModel.onEmailChanged(it) }
)
// Bidang kata sandi — serupa
PasswordField(
password = uiState.password,
onPasswordChange = { viewModel.onPasswordChanged(it) }
)
// Tombol — hanya menerima enabled (read-only)
LoginButton(enabled = uiState.isFormValid, onClick = viewModel::login)
}
}
// Komponen stateless: menerima email + callback
@Composable
fun EmailField(email: String, onEmailChange: (String) -> Unit) {
OutlinedTextField(
value = email,
onValueChange = onEmailChange,
label = { Text("Email") },
singleLine = true
)
}
// Komponen tombol stateless
@Composable
fun LoginButton(enabled: Boolean, onClick: () -> Unit) {
Button(onClick = onClick, enabled = enabled) {
Text("Masuk")
}
}
EmailField dan PasswordField — sepenuhnya stateless. Mereka dapat digunakan kembali di layar mana pun, terhubung ke sumber data mana pun. LoginButton menerima enabled sebagai read-only — ini adalah bentuk lain dari State Hoisting di mana state tidak diangkat (tombol tidak dapat mengaktifkan dirinya sendiri), tetapi dikirimkan dalam keadaan siap. Pendekatan ini memberikan fleksibilitas maksimum dengan kopling komponen yang minimal.
Tidak setiap state perlu diangkat. State lokal (State di dalam Composable) dibenarkan ketika: data hanya diperlukan di dalam satu komponen, tidak memengaruhi elemen tetangga, tidak harus bertahan dari rekomposisi bagian tertentu. Misalnya, state animasi, fokus bidang input, posisi scroll saat ini — masuk akal untuk disimpan secara lokal.
Kapan State Hoisting diperlukan: state digunakan oleh beberapa komponen anak; perubahan di satu anak harus tercermin di anak lain; logika perubahan state perlu diuji terpisah dari UI; state harus dipertahankan saat Activity dibuat ulang. Dalam kasus ini, state lokal menciptakan duplikasi dan inkonsistensi data.
Pendekatan hybrid: simpan state minimal secara lokal, angkat sisanya. Aturan Compose: “angkat state setinggi yang diperlukan dan serendah yang memungkinkan”. Dalam praktiknya, ini berarti memulai dengan remember lokal, dan hanya ketika muncul kebutuhan akses dari komponen lain — naikkan level. Jangan lakukan State Hoisting secara preventif — ini mempersulit kode tanpa perlu.
Pertanyaan yang Sering Diajukan
State Hoisting — adalah pola di tingkat komponen UI. ViewModel — adalah lapisan arsitektur untuk logika bisnis. State Hoisting dapat mengangkat state ke tingkat induk Composable, ke tingkat layar, atau ke ViewModel. ViewModel — adalah titik pengangkatan tertinggi untuk state yang harus bertahan dari pembuatan ulang Activity.
Komponen stateless diuji dengan meneruskan nilai secara sederhana. Panggil Composable dengan parameter yang diperlukan dan periksa tampilan melalui ComposeTestRule. Perubahan state diuji di tingkat induk atau ViewModel — terpisah dari UI. Ini sangat menyederhanakan pengujian: tidak perlu mensimulasikan rekomposisi di dalam komponen.
Ya, ini adalah praktik umum. Jika komponen hanya perlu menampilkan data tanpa kemampuan untuk mengubahnya — kirimkan State<T> (read-only). Komponen akan berlangganan perubahan, tetapi tidak dapat memulainya. Ini memperkuat enkapsulasi dan melindungi data dari mutasi yang tidak diinginkan.
Untuk pengiriman mendalam, gunakan CompositionLocal atau pengiriman melalui parameter induk Composable. Jika state diperlukan di seluruh layar — pindahkan ke ViewModel dan gunakan collectAsState(). Mengirimkan melalui 5+ level — tanda arsitektur yang salah; tinjau kembali hierarki komponen.
State Hoisting dapat sedikit meningkatkan jumlah rekomposisi, karena perubahan di induk dapat merekomposisi semua anak. Gunakan derivedStateOf untuk memfilter perubahan dan keys di LazyColumn untuk pembaruan titik. Di sebagian besar skenario, overhead State Hoisting dapat diabaikan dibandingkan dengan manfaat kemudahan pemeliharaan.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga