Belajar Jetpack Compose - Side Effects & Coroutines
Episode 10 of 23

Belajar Jetpack Compose - Side Effects & Coroutines

Episode ini menangani pekerjaan asinkron di Compose: LaunchedEffect, SideEffect, dan DisposableEffect, integrasi coroutines dengan UI, pola loading state untuk panggilan jaringan, serta efek yang sadar lifecycle.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Pendahuluan

Composable adalah fungsi murni: diberikan input, menghasilkan UI. Tapi aplikasi nyata perlu melakukan hal di luar komposisi — memanggil jaringan, mendengarkan listener, menulis ke file. Pekerjaan semacam ini disebut side effect, dan Compose menyediakan API khusus untuk menjalankannya dengan aman.

Melakukannya secara sembarangan — misalnya memanggil coroutine langsung di dalam composable — justru menimbulkan bug karena komposisi bisa dijalankan ulang dan dibatalkan kapan saja.

Episode 10 ini meliputi keluarga efek, integrasi coroutine, pola loading state, dan efek yang sadar lifecycle.

LaunchedEffect: Menjalankan Kode Asinkron

Efek yang Mengikuti Komposisi

LaunchedEffect menjalankan coroutine saat composable masuk komposisi dan membatalkannya saat keluar. Ini tempat yang tepat untuk pemuatan data awal:

KotlinLaunchedEffect untuk load data
@Composable
fun ArtikelScreen(viewModel: ArtikelViewModel) {
    val state by viewModel.uiState.collectAsState()
 
    LaunchedEffect(Unit) {
        viewModel.muatArtikel()
    }
 
    when (val s = state) {
        is UiState.Memuat -> LoadingIndikator()
        is UiState.Sukses -> KontenArtikel(s.artikel)
        is UiState.Gagal -> PesanError(s.pesan)
    }
}

LaunchedEffect(Unit) berjalan sekali saat komposisi dimulai. Kuncinya di parameter: jika key berubah, efek dibatalkan lalu dijalankan ulang. Pemakaian when pada sealed class ini adalah pola state UI yang akan dikupas di episode 20. Untuk memastikan dependency lifecycle terpasang, cek pohon dependency dengan ./gradlew :app:dependencies.

Ulang saat Key Berubah

Ketika key LaunchedEffect berubah, efek lama dibatalkan dan yang baru dijalankan. Pola ini berguna untuk menanggapi perubahan parameter:

KotlinLaunchedEffect dengan key
LaunchedEffect(artikelId) {
    muatKomentar(artikelId)
}

Saat artikelId berubah, coroutine lama dibatalkan dan pemuatan baru dimulai — mencegah data dari artikel lama menimpa yang baru.

SideEffect dan DisposableEffect

SideEffect untuk Kode Luar Komposisi

SideEffect dijalankan setelah komposisi berhasil — tempat yang tepat untuk memberitahukan kode di luar Compose:

KotlinSideEffect
SideEffect {
    onSetStatusBar(warnaStatusBar)
}

SideEffect { ... } dipanggil pada setiap komposisi yang berhasil. Berbeda dengan LaunchedEffect, kode di sini sinkron dan tidak boleh suspend — berguna untuk sinkronisasi dengan view system.

DisposableEffect untuk Cleanup

DisposableEffect menyediakan blok onDispose untuk membersihkan resource saat composable keluar dari komposisi:

KotlinDisposableEffect
DisposableEffect(Unit) {
    val listener = object : LocationListener {
        override fun onLocationChanged(p: Location) {
            posisiBaru(p)
        }
    }
    lokasiManager.tambahListener(listener)
 
    onDispose {
        lokasiManager.hapusListener(listener)
    }
}

onDispose menjamin listener dibuang ketika composable tidak lagi dipakai — mencegah kebocoran resource dan callback ke UI yang sudah hilang.

Coroutine dan Lifecycle

rememberCoroutineScope

Untuk meluncurkan coroutine dari event seperti onClick, gunakan rememberCoroutineScope. Scope ini otomatis dibatalkan saat composable meninggalkan komposisi:

KotlinrememberCoroutineScope
val scope = rememberCoroutineScope()
 
Button(onClick = {
    scope.launch {
        simpanKeServer()
    }
}) { Text("Simpan") }

rememberCoroutineScope() membungkus LaunchedEffect untuk penggunaan ad-hoc: coroutine berjalan di UI, dibatalkan otomatis saat composable hilang. Ingat pola ini sudah dipakai untuk Snackbar di episode 7.

collectAsStateWithLifecycle

Untuk meng-collect Flow hanya saat UI terlihat, gunakan collectAsStateWithLifecycle dari library lifecycle-runtime-compose:

KotlinCollect sadar lifecycle
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.9.0")
KotlincollectAsStateWithLifecycle
val lokasi by viewModel.lokasiFlow.collectAsStateWithLifecycle()
Text("Posisi: $lokasi")

collectAsStateWithLifecycle berhenti mengumpulkan saat UI tidak terlihat dan melanjutkan saat terlihat kembali — menghindari pekerjaan sia-sia di background dan menjadi rekomendasi resmi Android.

Pola Loading State

Satu State, Banyak Kondisi

Gabungkan efek dan state menjadi pola loading yang bersih: UiState membawa satu dari tiga kondisi, dan UI merender sesuai kondisi. Pola ini juga menjadi dasar MVVM dan MVI di episode 20.

KotlinSealed UI state
sealed interface UiState<out T> {
    data object Memuat : UiState<Nothing>
    data class Sukses<T>(val data: T) : UiState<T>
    data class Gagal(val pesan: String) : UiState<Nothing>
}

UiState mengekspresikan kondisi UI tanpa boolean yang saling bertabrakan. Dengan LaunchedEffect untuk memicu pemuatan dan when untuk rendering, alur data tetap mudah dilacak.

Penutup

Episode 10 menangani pekerjaan asinkron: LaunchedEffect untuk pemuatan berbasis key, SideEffect untuk sinkronisasi luar, DisposableEffect dengan onDispose untuk cleanup, rememberCoroutineScope untuk event ad-hoc, dan collectAsStateWithLifecycle untuk meng-collect Flow secara sadar lifecycle.

Inti yang harus dibawa pulang:

  • LaunchedEffect menjalankan coroutine mengikuti komposisi.
  • Ubah key LaunchedEffect untuk membatalkan dan memulai ulang efek.
  • DisposableEffect memastikan cleanup dengan onDispose.
  • rememberCoroutineScope membatalkan coroutine saat composable hilang.
  • collectAsStateWithLifecycle berhenti mengumpulkan saat UI tak terlihat.
  • Sealed UiState merangkum loading, sukses, dan gagal dengan rapi.

Di episode 11 selanjutnya kita akan membahas data persistence dan offline — integrasi Room dan DataStore, state restoration dengan rememberSaveable dan SavedStateHandle, pola offline-first dengan repository, serta UI yang digerakkan data.