← Назад к вопросам

Как запустить эффект в Compose

2.0 Middle🔥 181 комментариев
#UI и вёрстка#Жизненный цикл и навигация

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Запуск эффектов в Jetpack Compose

В Jetpack Compose эффекты (side effects) — это функции, которые позволяют выполнять действия, выходящие за рамки рекомпозиции, такие как подписки на потоки данных, анимации, одноразовые операции или взаимодействие с внешними API. Эффекты запускаются в ответ на изменения состояний и жизненного цикла компонентов.

Основные виды эффектов

1. LaunchedEffect — для корутин и асинхронных операций

Используется для запуска приостанавливаемых функций внутри Composable-функции. Автоматически отменяется при выходе из композиции или изменении ключей.

@Composable
fun TimerExample() {
    var seconds by remember { mutableStateOf(0) }
    
    LaunchedEffect(key1 = Unit) { // Запускается один раз при первом составлении
        while (isActive) {
            delay(1000)
            seconds++
        }
    }
    
    Text(text = "Прошло $seconds секунд")
}

2. DisposableEffect — для ресурсов с очисткой

Предназначен для эффектов, которые требуют очистки (dispose) при удалении из композиции или изменении ключей.

@Composable
fun SensorExample() {
    val sensorManager = LocalContext.current.getSystemService(Context.SENSOR_SERVICE) as SensorManager
    val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
    
    DisposableEffect(key1 = sensor) {
        val listener = object : SensorEventListener {
            override fun onSensorChanged(event: SensorEvent?) {
                // Обработка данных сенсора
            }
            override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
        }
        
        sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL)
        
        onDispose {
            sensorManager.unregisterListener(listener) // Важно очищать ресурсы!
        }
    }
}

3. SideEffect — для не-композиционных операций

Запускается после каждой успешной рекомпозиции, без возможности отмены. Полезен для синхронизации с внешними системами.

@Composable
fun AnalyticsTracker(isScreenVisible: Boolean) {
    val analytics = LocalAnalytics.current
    
    SideEffect {
        analytics.trackScreenVisibility(isScreenVisible) // Вызывается после каждой рекомпозиции
    }
}

4. rememberUpdatedState — для захвата актуального значения

Позволяет эффекту использовать самое актуальное значение, даже если оно изменилось после запуска эффекта.

@Composable
fun DelayedAction(onAction: () -> Unit) {
    val currentOnAction by rememberUpdatedState(newValue = onAction)
    
    LaunchedEffect(key1 = Unit) {
        delay(3000)
        currentOnAction() // Будет использовать актуальный onAction, даже если он изменился
    }
}

Практические паттерны использования

Запуск эффекта при изменении состояния:

@Composable
fun UserProfile(userId: String) {
    var userData by remember { mutableStateOf<User?>(null) }
    
    LaunchedEffect(key1 = userId) { // Перезапускается при изменении userId
        userData = userRepository.fetchUser(userId)
    }
    
    // Отображение данных пользователя
}

Комбинирование нескольких эффектов:

@Composable
fun ComplexScreen(isActive: Boolean) {
    // Эффект для подписки на данные
    LaunchedEffect(key1 = isActive) {
        if (isActive) {
            dataFlow.collect { /* обработка данных */ }
        }
    }
    
    // Эффект для анимации
    val animatedValue by animateFloatAsState(
        targetValue = if (isActive) 1f else 0f
    )
    
    // Эффект для логирования
    SideEffect {
        Log.d("ComplexScreen", "isActive changed to: $isActive")
    }
}

Ключевые правила использования:

  1. Всегда указывайте ключи (key1, key2, ...) для контроля перезапуска эффекта
  2. Избегайте побочных эффектов в теле Composable-функции — они должны быть внутри специальных эффектов
  3. Очищайте ресурсы в onDispose для DisposableEffect
  4. Не изменяйте состояние непосредственно в SideEffect — это может вызвать бесконечную рекомпозицию
  5. Используйте rememberCoroutineScope для запуска корутин вне эффектов:
@Composable
fun ButtonWithAction() {
    val scope = rememberCoroutineScope()
    
    Button(onClick = {
        scope.launch {
            // Асинхронная операция по клику
        }
    }) {
        Text("Выполнить")
    }
}

Выбор правильного эффекта

  • LaunchedEffect: для асинхронных операций, которые нужно отменить
  • DisposableEffect: для ресурсов, требующих очистки (подписки, слушатели)
  • SideEffect: для синхронизации с внешними состояниями
  • rememberCoroutineScope: для запуска корутин по событию (клик и т.д.)

Правильное использование эффектов обеспечивает предсказуемость, предотвращает утечки памяти и делает код более читаемым, разделяя логику рендеринга и побочные операции.