Qué son “estado” y “evento” en software • Diseñar state holders para nuestra app • Side effects principales • Diseñar la arquitectura a nivel de UI y de otras capas de nuestra app ¿Qué SÍ vamos a abordar?
del “estado” en Jetpack Compose • El estado define a la UI. • Composables dependen de variables que tienen un estado. • Lo dibujado es una representación gráfica del estado de nuestro programa.
UI State - Lo que quieres mostrar en la pantalla, encapsula el estado de los datos - Independiente del lifecycle de la vista - Preferentemente, referenciarlo en la clase ViewModel de Jetpack - Recibe datos de otras capas
Element State - Lógica de los elementos de nuestra interfaz gráfica - No sobrevive a los cambios de configuración* - Si la vista es compleja, se puede encapsular en una clase que actúe como state holder * A menos que implementes rememberSaveable
LoginFormState( email: String, password: String ) { var email by mutableStateOf(email) var password by mutableStateOf(password) … } var email by remember { mutableStateOf("") } var password by remember { mutableStateOf("") } var isEmailValid by derivedStateOf { /* logic for validation */ }
Ui State UI Element State - Estado de los datos - Independiente del lifecycle - Conectado con otras capas - Lógica de UI - Referencia a otros componentes de UI (como resources, navigation controller, etc) - Puede ser reusable
private fun AgeView( yearBorn: Int, onYearUpdated: (Int) -> Unit ) { LaunchedEffect(key1 = Unit, block = { delay(3000) // long running operation adultText = if (2022 - yearBorn > 17) { "Eres mayor de edad" } else { "Eres menor de edad" } }) Column { Text(text = adultText) TextField( value = yearBorn.toString(), onValueChange = { onYearUpdated(it.toInt()) } ) } } 1. AgeView es dibujado con un año de nacimiento inicial (2000). 2. El año se coloca en el TextField que es editable. 3. Antes de los 3 segundos, el usuario actualiza el año de nacimiento en el TextField a 2009. 4. Se ejecuta onYearUpdated en el composable padre, que cambia el valor del año de nacimiento 5. Al cambiar el año de nacimiento, AgeView se recompone con este nuevo valor ¿Qué sucede?
private fun AgeView( yearBorn: Int, onYearUpdated: (Int) -> Unit ) { LaunchedEffect(key1 = Unit, block = { delay(3000) // long running operation adultText = if (2022 - yearBorn > 17) { "Eres mayor de edad" } else { "Eres menor de edad" } }) Column { Text(text = adultText) TextField( value = yearBorn.toString(), onValueChange = { onYearUpdated(it.toInt()) } ) } } Se captura el valor inicial de yearBorn. Si AgeView se recompone, el LaunchedEffect sigue manteniendo el valor original.
private fun AgeView( yearBorn: Int, onYearUpdated: (Int) -> Unit ) { LaunchedEffect(key1 = Unit, block = { delay(3000) // long running operation adultText = if (2022 - yearBorn > 17) { "Eres mayor de edad" } else { "Eres menor de edad" } }) Column { Text(text = adultText) TextField( value = yearBorn.toString(), onValueChange = { onYearUpdated(it.toInt()) } ) } } Para capturar el último valor “actualizado”, se usa rememberUpdatedState.
private fun AgeView( yearBorn: Int, onYearUpdated: (Int) -> Unit ) { val updatedYearOld by rememberUpdatedState(newValue = yearBorn) LaunchedEffect(key1 = Unit, block = { delay(3000) // long running operation adultText = if (2022 - updatedYearOld > 17) { // we use the last value of yearBorn "Eres mayor de edad" } else { "Eres menor de edad" } }) . . . }
Ui State UI Element State - Independencia del lifecycle - Mantener y modificar datos a mostrar o manejar - Hay una fuente de datos externa a la capa de UI - UI se vuelve compleja - Agregar lógica de UI - Se quiere hacer testing de la lógica que puede tener nuestra UI data class LoginUiState( val errorInfo: LoginError? = null, val loggedIn: Boolean = false ) class LoginFormState( email: String, password: String, . . . ) { var email by mutableStateOf(email) var password by mutableStateOf(password) . . . }
Effect - Ejecutar algo con independencia de la recomposición o al cambiar el valor de un key. Esto porque ejecuta una coroutine por dentro. - Se inicia con la composición. - Es una función composable.
Coroutine Scope - Ejecutar coroutines en un composable. - Depende del ciclo de vida del composable que lo contiene. - Podemos controlar su ejecución (iniciarlo bajo un evento o cancelarlo).
Updated State - Cuando nuestro side effect depende de un valor que puede cambiar en el tiempo (o recompositions). - Si el valor de la variable de la que se depende cambia, nuestro side effect no se reinicia, mantiene la ejecución con el valor actualizado.