Comenzar explicando, que este artículo es completamente basado en mi experiencia personal a lo largo de los años que llevo en este mundillo de la programación. Cualquier duda o sugerencia puedes dejarla en los comentarios.
Dicho esto, explicaré desde mi punto de vista el por qué para mi, sobre-complicar los componentes puede ser el inicio del desastre en tu proyecto.
Al decir sobre-complicar un componente, me refiero directamente a la tendencia que tenemos muchos de crear componentes con la idea de ser utilizados y reutilizado en todos los lugares de la aplicación.
Creo que la mejor manera en que puedo mostrar esto, es mostrando un ejemplo con código de a lo que me refiero, y así no nos aburrimos (que somos programadores). A continuación tenemos el componente UserCard
que es utilizado en la esquina superior de la página para mostrar la imagen del usuario:
@Component({
selector: 'app-user-card',
templateUrl: './app-user-card.component.html',
styleUrls: ['./app-user-card.component.scss']
})
export class UserCardComponent {
@Input() userImageUrl: string;
constructor() {}
...
}
Este componente como se puede apreciar es bien simple y fácil de usar y entender por cualquiera que lo necesite usar. Pero qué pasa cuando hay que mostrar el card del usuario en el listado de usuarios, y mostrar más datos. Aquí empiezan a entrar nuevas variables en el componente:
@Component({
selector: 'app-user-card',
templateUrl: './app-user-card.component.html',
styleUrls: ['./app-user-card.component.scss']
})
export class UserCardComponent {
@Input() userImageUrl: string;
@Input() userName: string;
@Input() userLink: string;
@Input() isInList: boolean;
constructor() {}
...
}
Aquí empieza lo que llamo sobre-complicar el componente, comenzamos a introducir nuevas variables y con los nuevos datos que se necesitan y el conocido indicador que nos refiere si el componente tiene que comportarse como el inicial comportamiento o como el nuevo comportamiento (isInList). Y esto se repite mientras sigan surgiendo lugares donde se necesite dicho componente pero con diferentes variables o sencillamente estilos distintos.
Y este es el comportamiento que creo no debería seguirse, porque desde mi experiencia es el inicio del desastre. Este comportamiento puede fácilmente extrapolarse a otros lenguajes y tecnología, porque es lo mismo que reutilizar la misma función para diferentes acciones donde cada acción difiere de la anterior por pequeñas cosas pero sigue siendo la misma y por ello por qué duplicar código si podemos reutilizar el que ya está con más y más y más variable (que nunca terminan).
Y te preguntarás: ¿qué pasa con esto?, ¿cual es el problema?. Pues llega el momento en que esto se sale de las manos y el acoplamiento es tan grande, que cuando necesitamos cambiar comportamiento o estilo de dicho componente, clase o función, estamos en el inminente peligro de romper todos los demás comportamientos. Se llega incluso al punto en que es tan peligroso tocar algo en estos componentes, que se entra en el ciclo infinito de arreglar aquí y romper allá.
Creo que para explicar la solución a este problema, empezaré resolviendo esto en algo tan simple como una función y luego veremos como extrapolarlo a un componente visual en Angular. A continuación tenemos la función que retorna el listado de usuarios:
listOfUsers() {
return this.users.filter(user => user.isActive);
}
Como se puede constatar, en el ejemplo anterior obtenemos los usuarios activos del listado. Pero qué ocurre cuando también necesitamos en otro lugar los usuarios activos y con el role admin. Pues la tendencia siempre va a lo siguiente:
listOfUsers(isAdmin = false) {
if (isAdmin) {
return this.users.filter(user => user.isActive && user.role === 'admin')
}
return this.users.filter(user => user.isActive);
}
Y volvemos a lo mismo, cada vez que necesitemos un nuevo parámetro, continuamos alimentando a la función con más y más parámetros, cayendo en el mismo problema que antes mostré. Claramente este ejemplo es bien sencillo, pero espero entiendan la idea de lo que quiero mostrar.
¿Pero cómo resolver este problema? pues es bien sencillo, y es no sobre-complicar la función, y dejarla con su comportamiento inicial, y crear una nueva función que extienda este comportamiento. Veámoslo en la práctica:
listOfUsersWithRoleAdmin() {
return this.listOfUsers().filter(user => user.role === 'admin');
}
Como se puede ver en el ejemplo anterior, básicamente reutilizamos el código existente, y no sobre-complicamos la función. De esta manera, las funciones son lo suficientemente simples, como para no convertirse en un caos total en el futuro.
Y ahora te preguntarás, ¿pero cómo hago lo mismo con los componentes en Angular?
Angular en la actualidad ha evolucionado lo suficiente como para permitirnos una reutilización de todos las partes que conforman un componente, e incluso de los mismos componentes. Es posible, crear componentes que hereden de otros, reutilizando la lógica de los mismo y extenderlos, sin caer en sobre-complicar el componente. A continuación, un ejemplo de cómo podríamos afrontar el problema inicial del UserCard:
@Component({
selector: 'app-user-item-card',
templateUrl: './app-user-item-card.component.html',
styleUrls: ['./app-user-card-item.component.scss']
})
export class UserCardItemComponent extend UserCardComponent {
@Input() userName: string;
@Input() userLink: string;
constructor() {}
...
}
Como se puede apreciar, lo que hice en el ejemplo anterior, fue extender del componente base UserCard, para reutilizar la lógica existente y al mismo tiempo adicionar nueva lógica a este componente sin sobre-complicarlo. Incluso en los casos en que la vista y los estilos fuesen exactamente los mismo, podríamos reutilizarlos del original sin repetir este código tampoco.
De esta manera simple, es posible tener siempre componentes atómicos que no se conviertan en esos monstruo que luego nadie puede editar, porque romperías muchas cosas. Recuerda que esta solución puede ser extendida a otros lenguajes y tecnologías, no es solo para Angular con Typescript.
Tampoco empieces a repetir código por evitar este problema, porque si malo es sobre-complicar tus componentes, igual de malo y dañino, es duplicar código en tu aplicación. Claramente tienen diferentes impactos, pero todos repercuten de manera significativa en el proyecto a futuro.
Sin más, recuerda dejar tus comentarios si te fue interesante el artículo o si simplemente difieres con mi experiencia y crees que esto debería resolverse de otra manera o para ti no es un problema en lo absoluto.
Happy coding!