Побочные эффекты: LaunchedEffect
Узнаём, как правильно запускать побочные эффекты (загрузку данных, корутины) в Compose, не нарушая правила recomposition.
Побочный эффект — действие, выходящее за пределы отрисовки UI: сетевой запрос, запись в базу, запуск таймера или корутины.
Почему эффекты нельзя в теле composable
Как мы помним, тело composable может выполняться много раз. Если положить туда сетевой запрос, он повторится на каждой перерисовке. Для управляемого запуска эффектов Compose даёт специальные функции — effect handlers.
LaunchedEffect — запуск при входе
LaunchedEffect запускает корутину, когда composable впервые появляется на экране, и отменяет её при уходе. Он принимает ключ: при его смене эффект перезапускается:
@Composable
fun UserScreen(userId: String, viewModel: UserViewModel) {
LaunchedEffect(userId) {
viewModel.loadUser(userId) // выполнится при входе и при смене userId
}
// ... отрисовка экрана
}Здесь загрузка пользователя случится один раз при появлении экрана и повторно — только если изменится userId. На обычных перерисовках (например, при наборе текста в другом поле) эффект не сработает.
rememberCoroutineScope — эффект по событию
LaunchedEffect хорош для запуска «при входе». А если корутину нужно запустить в ответ на клик? Тогда берут rememberCoroutineScope — он даёт scope, привязанный к жизни composable:
@Composable
fun SnackButton(snackbarHostState: SnackbarHostState) {
val scope = rememberCoroutineScope()
Button(onClick = {
scope.launch {
snackbarHostState.showSnackbar("Готово!")
}
}) {
Text("Показать сообщение")
}
}Когда что использовать
| Инструмент | Когда |
LaunchedEffect(key) | запустить при входе / смене ключа |
rememberCoroutineScope | запустить корутину по событию (клик) |
Как работает под капотом
LaunchedEffect привязывает корутину к месту в композиции. Пока composable на экране и ключ не менялся, корутина продолжает жить. Уход composable из дерева отменяет её автоматически — это спасает от утечек и «висящих» запросов. rememberCoroutineScope возвращает scope, который тоже отменяется при выходе composable, но запускать корутины в нём вы можете вручную, из любого обработчика.
Частые ошибки
- Класть загрузку данных прямо в тело composable — она повторится при каждой перерисовке.
- Запускать в
onClickкорутину через произвольный scope вместоrememberCoroutineScope— рискуете утечкой после ухода экрана. - Передавать в
LaunchedEffectнеизменный ключUnit, когда эффект должен реагировать на смену параметра — он не перезапустится.
Итог
- Побочные эффекты нельзя писать в теле composable — только через effect handlers.
LaunchedEffect(key)запускает корутину при входе и перезапускает при смене ключа.rememberCoroutineScopeдаёт scope для запуска корутин по событиям (клик).