Documentation
$onMount / $onDispose
React to component lifecycle events.
$onMount
Run code when a component mounts:
var data: List<Show>? = null
$onMount {
data = $fetch("https://api.example.com/shows")
}
@if (data != null) {
<LazyColumn>
@for (show in data) {
<Text>{show.name}</Text>
}
</LazyColumn>
} else {
<CircularProgressIndicator />
} $onDispose
Clean up resources when component is removed:
var connection: WebSocket? = null
$onMount {
connection = WebSocket("wss://api.example.com")
connection?.connect()
}
$onDispose {
connection?.close()
} With Coroutines
Use launch for concurrent operations:
var shows: List<Show> = []
var isLoading = true
$onMount {
launch {
shows = $fetch("https://api.example.com/shows")
isLoading = false
}
} Multiple Effects
You can have multiple lifecycle hooks:
$onMount {
println("Component mounted")
}
$onMount {
launch {
data = loadData()
}
}
$onDispose {
println("Component disposed")
} Dependencies
Currently, $onMount runs once when the component mounts. For reactive effects based on state changes, use ViewModels or derived state:
var searchQuery = ""
var results: List<Result> = []
// This approach re-runs automatically when searchQuery changes
suspend fun search() {
if (searchQuery.isEmpty()) {
results = []
return
}
results = $fetch("https://api.example.com/search?q=$searchQuery")
}
// Call on state change
<TextField
bind:value={searchQuery}
onChange={() => launch { search() }}
/> Common Patterns
Data Loading
var posts: List<Post> = []
var error: String? = null
$onMount {
launch {
try {
posts = $fetch("https://api.example.com/posts")
} catch (e: Exception) {
error = e.message
}
}
} Polling
var status: String = "unknown"
$onMount {
launch {
while (true) {
status = $fetch("https://api.example.com/status")
delay(5000) // Poll every 5 seconds
}
}
}
$onDispose {
// Coroutine is automatically cancelled
} Event Listeners
var batteryLevel = 100
$onMount {
val receiver = BatteryReceiver { level ->
batteryLevel = level
}
registerReceiver(receiver)
}
$onDispose {
unregisterReceiver(receiver)
} Behind the Scenes
Whitehall transpiles lifecycle hooks to Compose APIs:
$onMount→LaunchedEffect(Unit) { ... }$onDispose→DisposableEffect(Unit) { onDispose { ... } }
For data loading that runs before screen render, use screen loaders instead of $onMount. Loaders provide better UX with automatic loading states.
Dispatchers
Run code on specific threads:
$onMount {
io {
// IO-bound work (network, disk)
val data = loadFromDatabase()
}
cpu {
// CPU-intensive work
val processed = processLargeDataset()
}
main {
// Update UI
updateState()
}
} See Also
- $data / $layoutData - Data loading patterns
- $fetch - HTTP requests
- $dispatch / $periodic - Background work