Jetpack Compose'da state yönetimi, UI'ın doğru ve verimli çalışmasının temelidir. State hoisting, state'i composeable fonksiyonlardan yukarı taşıyarak, unidirectional data flow (tek yönlü veri akışı) sağlayan bir desendir. Bu yaklaşım sayesinde state tek bir kaynakta toplanır, test edilebilirlik artar ve UI daha öngörülebilir hale gelir.
State Hoisting Nedir ve Neden Önemlidir?
State hoisting, bir composeable'ın içinde tanımlanan state'in, onu kullanan veya değiştiren tüm composeable'ların ortak bir üst seviyesine taşınmasıdır. Bu sayede state aşağıya doğru parametre olarak iletilir, değişiklikler ise callback fonksiyonlarıyla yukarıya bildirilir. Sonuç, temiz ve yönetilebilir bir kod yapısıdır.
Unidirectional data flow, state'in yalnızca bir yönde (üstten alta) aktığı, kullanıcı etkileşimlerinin ise callback'lerle yukarıya bildirildiği bir mimaridir. Bu desen hataları azaltır ve UI'ın durumunu tahmin edilebilir kılar.
Android ekosisteminde state hoisting, ViewModel ile birleştiğinde daha da güçlenir. ViewModel, UI state'i yaşam döngüsüne duyarlı bir şekilde tutar ve Compose ile entegre çalışır. Örneğin, bir ekranın yüklenme durumu, kullanıcı girdileri veya ağ sonuçları ViewModel'de StateFlow veya LiveData ile yönetilebilir.
State Hoisting'in Temel İlkeleri
- State Yukarı Taşınır: Bir composeable birden fazla alt bileşen tarafından paylaşılan state'e sahipse, bu state en yakın ortak üst composeable'a taşınır.
- State Aşağı Aktarılır: State, salt okunur parametre olarak alt composeable'lara iletilir.
- Olaylar Yukarı Bildirilir: Kullanıcı etkileşimleri (örneğin butona tıklama) callback fonksiyonlarıyla üst composeable'a iletilir.
Örnek: Basit Bir Sayaç Uygulaması
Aşağıda state hoisting uygulanmamış bir sayaç örneği yer alıyor:
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Tıklama: $count")
}
}
Burada state composeable'ın içinde kalmıştır. Dışarıdan erişilemez ve test edilmesi zordur. State hoisting ile state'i dışarı taşıyabiliriz:
@Composable
fun CounterScreen() {
var count by remember { mutableStateOf(0) }
Counter(
count = count,
onIncrement = { count++ }
)
}
@Composable
fun Counter(count: Int, onIncrement: () -> Unit) {
Button(onClick = onIncrement) {
Text("Tıklama: $count")
}
}
Artık Counter composeable'ı state'i yönetmez, yalnızca görüntüler. State, CounterScreen seviyesinde tutulur. Bu yaklaşım sayesinde Counter yeniden kullanılabilir ve test edilebilir hale gelir.
ViewModel ile State Hoisting
Gerçek uygulamalarda state genellikle ViewModel'de tutulur. ViewModel, UI state'ini StateFlow veya LiveData olarak dışarı verir. Compose'da collectAsState() ile bu state toplanır ve composeable'lara parametre olarak iletilir. Örneğin:
class MyViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
fun onUserAction() { /* state güncelle */ }
}
@Composable
fun MyScreen(viewModel: MyViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsState()
MyContent(state = uiState, onAction = viewModel::onUserAction)
}
Bu yapı, state'in tek bir kaynaktan yönetilmesini sağlar. ViewModel yaşam döngüsüne duyarlı olduğu için state konfigürasyon değişikliklerinde korunur.
LiveData vs StateFlow: Hangisi Ne Zaman Kullanılmalı?
| Özellik | LiveData | StateFlow |
|---|---|---|
| Compose Desteği | Evet (observeAsState) | Evet (collectAsState) |
| Null Başlangıç | Evet | Hayır (başlangıç değeri gerekli) |
| Lifecycle Aware | Evet (otomatik) | Manuel (repeatOnLifecycle) |
| Reaktif Akış | Sınırlı | Güçlü (operatörler) |
StateFlow, Compose ile daha uyumludur ve operatör desteği sayesinde daha karmaşık dönüşümler yapılabilir. LiveData ise eski projelerde veya basit durumlarda tercih edilebilir.
Unidirectional Data Flow'un Avantajları
- Test Edilebilirlik: UI state'i bağımsız olarak test edilebilir. ViewModel birim testleri, composeable UI testleri daha kolaydır.
- Bakım Kolaylığı: Veri akışı tek yönlü olduğu için hataların kaynağı hızlıca bulunur.
- Yeniden Kullanılabilirlik: State'i yönetmeyen composeable'lar farklı bağlamlarda rahatça kullanılabilir.
- Performans: Gereksiz yeniden oluşturmalar önlenir; Compose yalnızca değişen kısımları yeniden çizer.
Sık Yapılan Hatalar ve Dikkat Edilmesi Gerekenler
- State'i Çok Aşağıda Tutmak: State'i olabildiğince yukarı taşıyın. Gereksiz yere derin composeable'larda state tutmak yeniden kullanımı zorlaştırır.
- Callback'leri İhmal Etmek: State değişikliklerini doğrudan yapmak yerine her zaman callback kullanın. Bu, unidirectional flow'u bozar.
- View Model'i Composeable'a Gömmek: ViewModel'i mümkün olduğunca üst seviyede (örneğin ekran composeable'ı) tutun. Alt bileşenlere ViewModel enjekte etmekten kaçının.
- Aşırı State Kullanımı: Her küçük değişiklik için state oluşturmayın. immutable nesneler ve veri sınıfları kullanarak state'i tek bir nesnede toplayın.
State Hoisting ve Diğer Çerçevelerle Karşılaştırma
State hoisting, yalnızca Compose'a özgü değildir. Benzer desenler diğer modern UI kütüphanelerinde de bulunur. Örneğin, Flutter'da Riverpod ile state yönetimi de benzer bir felsefeye dayanır. Daha fazla bilgi için Flutter'da Riverpod ile Modern Durum Yönetimi yazımızı inceleyebilirsiniz. Ayrıca, React 19'da Server Components ve Client Components makalesi de veri akışı prensipleri açısından faydalı olabilir.
Sonuç
Jetpack Compose'da state hoisting, sağlam ve ölçeklenebilir UI mimarisi için olmazsa olmaz bir desendir. Unidirectional data flow ile state'in kontrolü merkezileşir, test edilebilirlik artar ve kod tekrarı azalır. ViewModel ile entegrasyon sayesinde bu desen Android projelerinde rahatlıkla uygulanabilir. Projenizde state hoisting prensiplerini benimseyerek daha temiz ve sürdürülebilir bir kod tabanı oluşturabilirsiniz.
Sık Sorulan Sorular
State hoisting ile ViewModel arasındaki fark nedir?
State hoisting, UI state'inin composeable'lar arasında yukarı taşınması desenidir. ViewModel ise bu state'i yaşam döngüsüne duyarlı bir şekilde saklayan ve iş mantığını yöneten bir nesnedir. Genellikle state hoisting uygulanırken ViewModel, state'in en üst sahibi olarak kullanılır.
State hoisting her zaman ViewModel ile mi yapılmalı?
Hayır. Basit ekranlar için remember ve mutableStateOf yeterli olabilir. Ancak state'in karmaşıklığı arttıkça veya konfigürasyon değişikliklerinde korunması gerekiyorsa ViewModel kullanmak daha uygundur.
LiveData mı StateFlow mu tercih edilmeli?
Yeni projelerde StateFlow önerilir çünkü Compose ile daha uyumludur, reaktif operatörler sunar ve null güvenliği sağlar. LiveData ise mevcut projelerde veya basit kullanım durumlarında tercih edilebilir.
State hoisting performansı nasıl etkiler?
Doğru kullanıldığında state hoisting gereksiz yeniden oluşturmaları önleyerek performansı artırır. Compose yalnızca değişen state'e bağlı bileşenleri yeniden çizer, bu sayede UI daha verimli çalışır.






