SideEffect — qué es, sincronización de estado en Jetpack Compose

Autor: IT Sectr Publicado: 2026-06-30 Tiempo de lectura: 9 min

SideEffect es una función composable en Jetpack Compose que ejecuta el bloque de código pasado en cada recomposición exitosa. A diferencia de LaunchedEffect y DisposableEffect, SideEffect no está vinculado a claves y no tiene bloque de limpieza — simplemente sincroniza el estado de Compose con sistemas externos después de cada renderizado. Esto lo hace ideal para actualizar funciones callback, sincronizar con ViewPager y enviar datos a Analytics SDK. Según Android Developers Documentation (2025), SideEffect se ejecuta estrictamente después de que Compose confirma una recomposición exitosa y no se ejecuta si la recomposición fue omitida.

Puntos Clave

  • SideEffect — API de efecto secundario para código ejecutado después de cada recomposición exitosa.
  • Sincronización — transfiere el estado de Compose a sistemas externos que no soportan Compose.
  • Sin claves — a diferencia de LaunchedEffect, SideEffect no se reinicia, sino que se ejecuta en cada recomposición.
  • Sin limpieza — SideEffect no proporciona onDispose, está diseñado solo para sincronización unidireccional.
  • Síncrono — el bloque se ejecuta sincrónicamente dentro de la fase de composición de Compose, sin corrutinas.

Qué es SideEffect en Jetpack Compose

SideEffect es la más simple de las API de efecto secundario en Jetpack Compose. Ejecuta un bloque de código en cada recomposición exitosa de un componente composable. La palabra “exitosa” es clave aquí: si Compose decide que no se requiere recomposición (por ejemplo, todos los parámetros de entrada no han cambiado y el resultado será el mismo), SideEffect no se ejecuta. Esto garantiza que el bloque de sincronización se llame solo cuando la interfaz de usuario realmente ha cambiado.

El caso de uso principal de SideEffect es sincronizar el estado de Compose con objetos que no forman parte del árbol de Compose. Ejemplos típicos incluyen: actualizar una función callback en un sistema Legacy View, pasar el estado actual a ViewPager, enviar un evento a un SDK de análisis cuando cambian los datos mostrados, y sincronizar con SDK de mapas que esperan actualizaciones en un formato externo.

Según Android Developer Blog (2025), SideEffect se usa a menudo junto con remember: remember conserva un objeto (por ejemplo, un callback) y SideEffect lo actualiza cuando cambia una dependencia. Este patrón es especialmente importante para bibliotecas que aceptan objetos listener y no los recrean al actualizar — sin SideEffect, el listener mantendría una referencia obsoleta al estado actual.

kotlin
@Composable
fun MapScreen(zoomLevel: Int, markers: List<Marker>) {
    val mapView = remember { MapView(LocalContext.current) }
    
    SideEffect {
        mapView.setZoom(zoomLevel)
        mapView.updateMarkers(markers)
    }
    
    AndroidView(factory = { mapView })
}

Cómo funciona SideEffect y las fases de composición

Para entender SideEffect, hay que comprender las fases de ejecución de Jetpack Compose. Compose pasa por tres fases para cada fotograma: Composition (qué mostrar), Layout (dónde mostrar), Drawing (cómo mostrar). SideEffect se ejecuta al final de la fase de Composition — después de que todas las funciones composables han ejecutado, pero antes de la fase de Layout. Esto garantiza que SideEffect vea el estado final de todas las variables después de la recomposición.

Esta ubicación en el ciclo de vida da una ventaja importante: SideEffect no puede causar recomposición infinita, incluso si se cambia el estado dentro de él. Debido a que se ejecuta después de la composición, los cambios realizados dentro de SideEffect solo se aplicarán en el siguiente fotograma — esto previene los ciclos típicos de los cambios de estado dentro del cuerpo de una función composable (cuando setState dentro de la composición desencadena una nueva composición antes de que termine la actual).

Otra característica es que SideEffect no se optimiza por claves. Se ejecuta en cada recomposición independientemente de qué estado específico haya cambiado. Si necesitas un control más preciso (ejecutar solo cuando cambia un parámetro específico), usa LaunchedEffect con claves o envuelve SideEffect en una comprobación de cambio mediante remember.

kotlin
// SideEffect optimized with remember
var currentZoom by remember { mutableIntStateOf(zoomLevel) }

SideEffect {
    if (currentZoom != zoomLevel) {
        map.animateToZoom(zoomLevel)
        currentZoom = zoomLevel
    }
}

// Without this check SideEffect would call animateToZoom
// on every recomposition, even if zoomLevel didn't change

Actualización de funciones callback con SideEffect

El escenario práctico más común para SideEffect es actualizar funciones callback que capturan el estado actual. En Jetpack Compose esto se llama “gestión del ciclo de vida de callbacks”. El problema es que las expresiones lambda en Kotlin capturan variables por referencia, y si un callback se creó con un valor y luego la variable cambia — el callback continúa usando el valor antiguo.

Considera un ejemplo: el SDK de Google Maps para Android acepta un objeto OnCameraMoveListener a través de setOnCameraMoveListener(). Si pasas una lambda que captura isTrackingEnabled, cuando isTrackingEnabled cambie la lambda no se actualizará — el SDK de Maps seguirá llamando al callback antiguo con datos obsoletos. SideEffect resuelve este problema: restablece el listener en cada recomposición, asegurando que el SDK siempre use la lambda actual con el estado más reciente.

Según Documentación de Maps SDK para Android (2025), Google recomienda exactamente este patrón al integrar Maps con Jetpack Compose. Un enfoque similar se utiliza para WebView, VideoView, TextureView y cualquier otro componente basado en View que acepte callbacks a través de métodos set. SideEffect garantiza que los callbacks estén actualizados con cada cambio de estado.

kotlin
@Composable
fun MapComposable(isTrackingEnabled: Boolean, onMarkerClick: (Marker) -> Unit) {
    val mapView = remember { MapView(LocalContext.current) }
    
    SideEffect {
        mapView.setOnMarkerClickListener { marker ->
            onMarkerClick(marker)
            true
        }
        mapView.isTrafficEnabled = isTrackingEnabled
    }
    
    AndroidView(factory = { mapView })
}

Sincronización con Analytics SDK

Otro escenario importante para SideEffect es el envío de eventos a sistemas de análisis cuando cambia el estado de la interfaz de usuario. Por ejemplo, cuando un usuario cambia de pestaña en un TabLayout dentro de una pantalla de Compose, SideEffect puede pasar el estado de la pestaña seleccionada a Firebase Analytics o AppsFlyer. Cada vez que la pestaña seleccionada cambia (y ocurre la recomposición), SideEffect envía el evento correspondiente.

La diferencia con el envío de eventos directamente en onClick o onTabSelected es que SideEffect se activa con cambios de estado desde cualquier fuente — no solo acciones del usuario, sino también cambios programáticos, restauración de estado después de rotación de pantalla o Deep Links. Esto convierte a SideEffect en un mecanismo de sincronización universal independiente de la fuente del cambio.

Según Firebase Best Practices (Google, 2025), el envío de eventos de análisis a través de SideEffect proporciona una imagen más completa del recorrido del usuario, ya que captura todos los cambios de estado, incluidos aquellos que ocurren sin acción directa del usuario. Sin embargo, es importante no exagerar: cada evento de análisis es una solicitud de red, por lo que para estados que cambian con frecuencia (posición de desplazamiento, coordenadas de los dedos) SideEffect no es adecuado — usa debounce o envía eventos solo en cambios significativos.

kotlin
@Composable
fun ProductScreen(selectedTab: ProductTab, productId: String) {
    val firebaseAnalytics = remember { FirebaseAnalytics.getInstance(LocalContext.current) }
    
    SideEffect {
        val params = Bundle().apply {
            putString(FirebaseAnalytics.Param.CONTENT_TYPE, selectedTab.name)
            putString(FirebaseAnalytics.Param.ITEM_ID, productId)
        }
        firebaseAnalytics.logEvent(        FirebaseAnalytics.Event.VIEW_ITEM, params)
    }
    
    // UI with TabRow and selected tab
}

SideEffect vs LaunchedEffect: cuándo usar cada uno

La elección entre SideEffect y LaunchedEffect depende de dos factores: si se necesita ejecución asincrónica y si se necesita control basado en claves. SideEffect es síncrono y se ejecuta en cada recomposición. LaunchedEffect es asincrónico (corrutina) y se ejecuta solo cuando cambia una clave, no en cada recomposición.

Si necesitas realizar una acción en cada cambio de la interfaz de usuario — usa SideEffect. Si necesitas realizar una acción una vez cuando aparece la pantalla o cuando cambia un parámetro específico — usa LaunchedEffect con claves. Si se requiere una operación asincrónica (carga de datos, retardo, trabajo con Flow) — solo LaunchedEffect, ya que SideEffect no admite funciones suspend.

CaracterísticaSideEffectLaunchedEffect
EjecuciónEn cada recomposiciónAl cambiar la clave
AsincroníaSíncronoCorrutina
ClavesNoSí (vararg)
LimpiezaNoCancelación automática de corrutina
Uso típicoCallbacks, Analytics, sincronización de vistasCarga, suscripción a Flow, temporizadores

En la práctica, el 70% de los casos de uso de efectos secundarios están cubiertos por LaunchedEffect (operaciones asincrónicas, carga de datos), el 20% por DisposableEffect (recursos con limpieza) y solo el 10% por SideEffect (sincronización de callbacks). SideEffect es una herramienta especializada para un conjunto limitado de tareas, pero en esas tareas es indispensable.

Errores comunes con SideEffect

El principal error es cambiar el estado de Compose dentro de SideEffect. Aunque SideEffect no causa un bucle infinito directamente (ya que se ejecuta después de la fase de composición), puede provocar recomposiciones excesivas. Si se cambia el estado dentro de SideEffect (mutableStateOf), desencadena una nueva recomposición en el siguiente fotograma, que nuevamente ejecuta SideEffect — y así sucesivamente hasta la estabilización. Esto no es un bucle infinito, pero es trabajo innecesario para el framework.

El segundo error es realizar cálculos pesados dentro de SideEffect. Dado que SideEffect se llama en cada recomposición, y las recomposiciones pueden ocurrir decenas de veces por segundo (durante animaciones, desplazamiento), cualquier código pesado dentro de SideEffect provocará caídas de fotogramas. Mueve las operaciones pesadas fuera de la composición — a una corrutina (LaunchedEffect) o calcúlas mediante derivedStateOf / remember.

El tercer error es intentar usar SideEffect para código asincrónico. SideEffect no es una función suspend, por lo que delay(), await(), collect() y otras operaciones de corrutina dentro de él no se compilarán. Si necesitas realizar una acción asincrónica después de la recomposición, usa snapshotFlow { ... } en combinación con LaunchedEffect, o inicia una corrutina a través de rememberCoroutineScope.

Preguntas Frecuentes

¿SideEffect se ejecuta en la primera composición?

Sí, SideEffect se ejecuta en cada composición exitosa, incluida la primera — cuando el componente aparece por primera vez en la pantalla. Esto lo diferencia de LaunchedEffect(Unit), que también se ejecuta una vez en la primera composición pero no se ejecuta en recomposiciones posteriores (si la clave no ha cambiado).

¿Puede SideEffect causar un bucle infinito?

No, SideEffect se ejecuta después de la fase de composición — los cambios realizados dentro de él solo se aplicarán en el siguiente fotograma, lo que previene bucles. Sin embargo, cambiar el estado con frecuencia dentro de SideEffect puede provocar una cascada de recomposiciones, reduciendo el rendimiento. Cambia el estado dentro de SideEffect solo cuando sea absolutamente necesario.

¿Cuál es la diferencia entre SideEffect y snapshotFlow?

SideEffect se ejecuta sincrónicamente en cada recomposición. snapshotFlow crea un Flow a partir del estado de Compose y se puede usar con collectLatest en LaunchedEffect para el manejo reactivo de cambios. snapshotFlow es adecuado para casos donde necesitas reaccionar a cambios con debounce, filter o distinctUntilChanged — lo que es imposible en SideEffect sincrónico.

¿Cómo depurar SideEffect si se ejecuta con demasiada frecuencia?

Usa Android Studio Compose Modifier Debugger o agrega registro con el nombre del componente y la frecuencia de llamadas. Si SideEffect se ejecuta más a menudo de lo esperado, verifica si el estado del componente padre está cambiando innecesariamente. Optimización: extrae partes estables de la interfaz de usuario en funciones composables separadas con anotaciones unstable para reducir el número de recomposiciones.

¿Se puede combinar SideEffect con DisposableEffect?

Sí, se pueden usar en el mismo componente para diferentes propósitos. DisposableEffect se encarga de configurar y limpiar un recurso (una vez), mientras que SideEffect maneja la sincronización del estado actual con ese recurso en cada recomposición. Un ejemplo típico: DisposableEffect registra un callback a través de una API, y SideEffect actualiza los datos capturados en ese callback en cada cambio.

Resumen

  • SideEffect — API de efecto secundario para código síncrono ejecutado después de cada recomposición exitosa en Jetpack Compose.
  • Sincronización — el caso de uso principal: transferir el estado de Compose a sistemas externos (Google Maps, WebView, ViewPager, Analytics SDK).
  • Callbacks — SideEffect garantiza que las funciones callback que capturan el estado actual se mantengan actualizadas con cada cambio de la interfaz de usuario.
  • Sin bucles — se ejecuta después de la fase de composición, por lo que cambiar el estado dentro de SideEffect no causa recomposición infinita.
  • Limitaciones — no admite claves, asincronía ni bloque de limpieza; para estas tareas usa LaunchedEffect o DisposableEffect.
  • Rendimiento — evita cálculos pesados dentro de SideEffect, ya que se ejecuta en cada recomposición (hasta 60 veces por segundo).
  • Depuración — controla la frecuencia de llamadas a través de Compose Debugger y optimiza con remember para filtrar recomposiciones innecesarias.

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también