Repensar las acciones de los usuarios para diseñar un mejor flujo de datos unidireccional | de Rygel Louv | abril 2022

Contenidos

Las acciones del usuario deben tener un lugar importante en la implementación de UDF.

Imagen minimalista de Samantha Gades en Unsplash

Descargo de responsabilidad: este artículo se publicó originalmente como la segunda parte de este artículo, pero cuando lo piensas bien, estas dos historias no tienen mucho en común y son completamente independientes. Así que se cambió el título y se desvinculó el artículo.

El flujo de datos unidireccional ya no es un patrón que debamos adoptar. Este concepto ingresó al mundo de Android junto con el patrón de arquitectura MVI. La arquitectura MVI está creciendo en popularidad e incluso hoy en día podemos ver claramente que algunos de sus conceptos son ampliamente utilizados incluso por Google. Porque MVI fue el primer patrón arquitectónico en proponer la idea de gestión de estado tal como la conocemos hoy en el mundo Android. El flujo de datos unidireccional expone la idea de que el flujo de datos debe ser en una sola dirección. Es decir, asumimos un estado inicial de la pantalla de la IU, el usuario interactúa con la pantalla, se procesan las acciones del usuario y se crea un nuevo estado de la IU.Imagen de https://proandroiddev.com/android-unidireccional-state-flow-without-rx-596f2f7637bb En este artículo (léalo si tiene tiempo) he argumentado que para un código de interfaz de usuario más limpio, uno debe asegurarse de representar la interfaz de usuario como un un objeto de un solo estado que de otro modo podría ser mutado podría terminar en un lío. Recuerde que el objetivo es una vista pasiva, un código de interfaz de usuario tonto y tonto que principalmente muestra datos y captura la entrada del usuario. Hacer algo como esto 👇 sin duda agrega complejidad y, en última instancia, debemos evitarlo:Fuente: Charla de Kaushik Gopal sobre UDFAPero, en cambio, queremos que nuestras vistas de la interfaz de usuario estén más organizadas y queremos tener una idea clara de cómo fluyen los datos en el sistema.Fuente: conferencia de Kaushik Gopal sobre UDF Si lee la documentación de Google, le proporcionará algunas explicaciones claras y ejemplos sobre UDF, pero hay una cosa que me molesta y es la forma en que se representan las acciones del usuario. Piénselo, dicen que el estado de la interfaz de usuario debe representarse adecuadamente. Entonces creamos clases de datos o clases selladas, objetos para representar cada posible mutación de UI. Pero cuando se trata de acciones del usuario, la mayoría de los ejemplos que verá simplemente permiten que la interfaz de usuario llame a las funciones de ViewModel. Creo que las acciones del usuario (a las que algunos se refieren como eventos de IU o intenciones en MVI) también deben modelarse usando clases de datos, objetos y clases selladas, y argumentaré que esto ayuda a proporcionar un código mucho más limpio. Primero implementamos UDF porque queremos un código más predecible que se escale fácilmente. En UDF, su ViewModel debe exponer solo un objeto para la observación del estado, y también creo que la vista solo debe llamar a una función para proporcionar las acciones del usuario. Básicamente, Una puerta de entrada y una puerta de salida.

¿Cómo lo haremos?

Primero definimos las acciones. Luego, podemos configurar nuestro ViewModel para consumir acciones a través de un canal de Kotlin: así que todavía tenemos las funciones, pero ahora podemos hacerlas todas privadas excepto la ProcesoAcción función, que ahora es la única función pública expuesta por ViewModel.

¿Cuál es el beneficio de esto?

Como puede ver, en realidad no ha cambiado mucho. Pero para explicar cómo esto puede ayudar a que el código sea más limpio, tomemos un ejemplo en Jetpack Compose. En Jetpack Compose, se nos anima a hacer que nuestro componible no tenga estado al enviar el estado de componible a la persona que llama. así es como lo llamamos levantamiento de estado.Entonces, básicamente, está pasando los valores de estado al componible junto con las funciones que se ejecutarán cuando ocurra un evento de IU específico (que llamo acciones en esta publicación). El documento oficial de Android tiene un brillante ejemplo de esto: @Composablefun HelloScreen() {Var Name from RememberSaveable { MutableStateOf(«») }HelloContent(Name = Name, onNameChange = { Name = it })}@Composablefun HelloContent(Name: String , onNameChange: (String) -> Unidad) {Columna(modificador = Modifier.padding(16.dp)) {Text(texto = «Hola, $nombre»,…)OutlinedTextField(valor = nombre,onValueChange = onNameChange, label = { Text(«Name») })}}Pero aunque la elevación del estado es definitivamente un concepto muy bueno e importante, creo que ya te habrás encontrado con el tipo de problema que puede causar. Supongamos que tenemos un componible principal que tiene varios componibles secundarios y toma las funciones de eventos de la interfaz de usuario como parámetros y los pasa a los diversos componibles secundarios. Así es como debe hacerse después de la elevación del estado: como puede ver, el elemento componible MoviesScreenContent tiene muchos parámetros y este número crece en proporción a la cantidad de elementos componibles secundarios. Si tiene Detekt instalado, es probable que obtenga un error de LongParameterList. Y ese es el tipo de problema que las Acciones y ViewModel configurados anteriormente pueden solucionar. ¿Qué pasa si acabamos de pasar un parámetro para activar una acción (evento de interfaz de usuario) cuando sucede? Para hacer esto, necesitamos configurar el sistema de acción en el lado de la interfaz de usuario: notará que creamos uno canal de acción objeto que usamos para alimentar las acciones llamando al ViewModel viewModel.processAction (acción de película) y eso es todo lo que tenemos que hacer. Ahora podemos simplemente pasar el actionChannel a los componibles secundarios en lugar de las funciones de Lambda: ¿Y cómo notificamos cuando el usuario realiza una acción? Simple, solo pégalo en el canal. Con esta refactorización, en lugar de pasar las funciones de ViewModel como lambdas, puede simplemente pasar el actionChannel y exponer sus acciones a pedido. Gracias a la configuración que hicimos anteriormente, cada vez que se llama a actionChannel.tySend(MoviesAction), se llama automáticamente a viewModel.processAction(MoviesAction), lo que activa la función correcta en ViewModel. ViewModel.processAction() es nuestra única puerta de entrada y ViewModel .state es nuestra única puerta de salida.

También funciona con el sistema View

Por supuesto, también puede usar esta refactorización en el sistema View. Puedes beneficiarte de ello. Enlaces de flujoy fusione todos los flujos de eventos de la interfaz de usuario y el flujo de actionChannel en un solo flujo. Los fusiona porque solo necesita un flujo para sus acciones.

¿Qué hay de los eventos de un solo tiro?

Para eventos de disparo único, como los que se usan para mostrar una barra de refrigerios, el documento oficial de Android tiene un ejemplo de dónde se trata como parte del estado de la interfaz de usuario, y recomendaron rastrear los eventos mediante un mecanismo grueso. No me gusta mucho esta solución, aunque respeta el patrón UDF, todavía me parece más una solución alternativa. Prefiero un objeto de evento SharedFlow separado usado solo para eventos de disparo único. O use los elementos SideEffect que vienen con Compose. Depende de usted. Actualización: también puede seguir adelante y llamar a la función ProcessAction() de ViewModel directamente, sin configurar necesariamente el flujo. Eso lo haría aún más fácil si no necesita un flujo. Flow es particularmente útil para mí cuando uso esta técnica en el sistema View junto con FlowBindings. Pero siéntase libre de elegir una versión más simple de esto, siempre y cuando retrate sus acciones correctamente. Gracias a los lectores que señalaron esto en los comentarios 🙏 En este artículo, discutimos la necesidad de modelar sus acciones de usuario de la misma manera que lo hace con el estado de su IU, y discutimos cómo esto podría ayudar a que su código de IU sea fácil de mejorar y configurar un flujo de datos unidireccional más claro. Tenga en cuenta que las ideas discutidas aquí son muy idiosincrásicas, es posible que tenga una perspectiva diferente y, si ese es el caso, me encantaría escucharlo en los comentarios. Gracias por leer.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.