Manejador de estados: Primer acercamiento
Publicado en
Conocimientos previos recomendados: React, Typescript
Este desarrollo nació de la necesidad de agrupar un conjunto de tareas por estado y que sea fácil moverlas entre los mismos. Quien no movió una tarea en un tablero de Trello o Jira no tuvo infancia 😆.
Primero, creemos el proyecto. Esto se puede hacer simplemente con React , pero por comodidad y costumbre lo armo en Next. Para crearlo usemos el comando que nos provee Next:
Cuando creamos el proyecto, nos hace una serie de preguntas.
De estas configuraciones iniciales podemos sacar dos puntos interesantes para charlar.
-
Typescript VS Javascript: Tipar o no Tipar esa es la cuestión
-
Tailwind css: ¿Ahorrar tiempo o comprar problemas?
Mi opinión sobre Typescript es que es mejor tipar todo lo que se pueda y para lo que no, siempre tenemos a nuestro fiel amigo any
para salvarnos las papas del fuego. Un código con muchos any's es lo mismo que no haber querido usar TS, por eso recomiendo eventualmente
reemplazarlos por su correcta interfaz.
Con respecto a Tailwind css me pasa que como soy bastante nuevo en CSS, esta herramienta me brinda una buena escala de colores, padding, font, etc, para ordenarme. Se que mi pata más débil es el css y tengo pensado reforzarla con el curso CSS for JavaScript Developers (Aguante Josh 😅).
Sugerencia para el lector: Tomémonos un momento para pensar que modelos de datos podríamos crear para representar las distintas partes del problema (Las tareas y los distintos estados para cada tarea).
Los modelos de datos que se me ocurren para modelar son Tarea/Card y las Columnas donde las agrupamos por Estado:
La interfaz ICard nos permite caracterizar, el texto, el estado y el id de la tarea. El estado inicial es un atributo opcional que decidí agregar para poder reiniciar una tarea. Podríamos intentar abstraer las Columnas/Estados con una interfaz, pero esta primera implementación dejemosla así y cuando terminemos revisemos qué más podríamos mover a una interfaz.
Si observan los tipo del atributo status y initStatus, no son un tipo primitivo de dato (string, number, etc). Para caracterizar el estado de una tarea cree un enum con los estados de mi tablero:
Ahora creemos el componente StatusManager. Para crear un componente de manera rápida y ordenada uso la lib new-component, corriendo el siguiente comando, que configure en el package.json, para ejecutarlo con npm:
Bien ya tenemos el componente. Invoquemos a StatusManager
en el page, pasandole una lista de tareas de prueba.
Sugerencia para el lector: Paremos a meditar qué es lo queremos construir. Hagámonos las siguientes preguntas . Si tuvieras que separar en partes el problema ¿cuáles son y cómo las llamarías? ¿Cuáles son las partes que se repiten? Tomate tu tiempo, una vez que tengas una respuesta seguí leyendo.
Ahora empieza lo interesante, es momento de armar los componentes de React. Los componentes que use son Card y Column tratando de representar la tarea y la columna/estado a la cuál movemos las tareas.
¿Qué responsablidad y qué datos debería tener el componente Card? El componente debería tener el texto de la tarea y una funcion para cambiarle el estado. Para cambiar el estado voy a usar tres botones:
-
Un flecha que indique a la derecha para avanzarlo de estado.
-
Una cruz para moverle a la columna de tareas congeladas ó paradas.
-
Un reload para reiniciarla.
Les dejo el codigo por si tienen dudas, les recomiendo fuertemente que antes de leerlo intenten hacer su propia implementeacion 😸 :
Componente Card
La función que se encarga de manejar ese cambio de estado la voy a delegar a su padre (StatusManager), recibiéndola como parámetro. Una pregunta valida seria ¿Porque tome esa decision? a lo que nos lleva a hablar de responsablidad de componentes.
Como vemos el punto de contacto entre Card y Column es StatusManager, él es quien tiene la responsabilidad de administrar las tareas, pasandole a cada Card sus datos ademas de una forma de avisarle cuando la misma cambie de estado y le da a Column la lista de tareas que debe mostrar.
En StatusManager vamos a agrupar las tareas por estado para poder mostrarlas en las columnas:
En el componente Column vamos a tener el título y la estética que queramos darle a cada columna para diferenciarlas. Luego de agrupar las tareas podemos invocar a Column pasando como children la lista de Cards:
Lo último que falta definir es la función que cambia de estado una tarea:
Analicemos que hace esta función, primero busca la tarea por id y en caso de no encontrarlo termina la función con un return. Luego valida que le hayan pasado un estado. A partir de la vieja tarea crea una copia con el estado nuevo, después crea una copia de la lista de tareas quitando la vieja tarea, agrega la nueva al principio de la copia de la nueva de lista y por último llama a setCards pasandole la nueva lista.
Una observación que haría es "Nunca volviste a filtrar por estado, ¿Cómo es que las listas que agrupa por estado actualizaron sus tareas?". Esto pasa porque guarde las tareas dentro de useState.
Por cómo funciona React, cuando modificas un estado vuelve a actualizar sus dependencias y corre de nuevo la
función StatusManager, haciendo también que se actualicen nuestras listas const
.
Demo:
Frozen
Pending
task 1
task 2
task 3
In Progress
Done
Si tienen dudas o se trabaron al implementarpueden revisarlo en el git en el branch without-framer-motion .
Ahora que ya hicimos nuestra primera implementación podemos pensar si vale la pena agregar una interfaz para la Columna. En la misma podríamos ponerle el nombre de la columna, el color de fondo y otros aspectos de la columna que decidan agregar:
Yo compuse el status con las partes del css, así el status define el fondo y el color del texto.
Los beneficios que trae abstraerlo en una interfaz es que podemos crear un array de IColumn e iterarlo ahorrandonos tener que escribir y filtrar por cada columna , asi reducimos la cantidad de codigo repetido, Además agregar una nueva columna es más sencillo, ya que ahora solo tenemos que agregar nuestra columna al array y definir su css.
Les recomiendo que intenten hacerlo como ejercicio para solidificar lo aprendido. No hay mejor manera para aprender que ensuciándose las manos con código 💪 .
Como se puede ver en la demo, las cards cambian instantáneamente de Columna a Columna, lo cual es feo visualmente. En el próximo Post vamos a meternos en el excitante mundo de animaciones con framer motion 😎.
Si quieren contactarme para darme feedback pueden escribirme a (mlpaz.code@gmail.com)
.
Para enterarse cuando hay nuevo post siganme en Instagram.
Espero que les haya gustado y nos vemos en el próximo post.
🖖 Mr Peace and code
Nota: El diagrama de componentes fue generado con la libreria React Flow.