32

3 patrones elementales de desarrollo de componentes que usamos en React

2351Puntos

hace 5 años

El progreso de un lenguaje de programación típicamente ocurre a través del descubrimiento de patrones. Cuando los encontramos, tratamos de empaquetarlos en librerías para luego reutilizarlos. Esto evita estar escribiendo el mismo código una y otra vez. También, estas nuevas abstracciones nos ayudan a esconder la complejidad del patrón dentro de funciones o clases.

Una librería no es el fin de este ciclo de desarrollo. De hecho, también puede ser el comienzo de nuevos patrones y librerías. jQuery, por ejemplo, surge como una mejor abstracción para operar sobre el DOM. Con jQuery podemos dejar de preocuparnos por el manejo de las diferencias entre navegadores (el patrón), y comenzar a usar funciones (abstracciones) que nos facilitan la vida. Pero incluso dentro de jQuery hay un sistema de plugins que nos permite esconder patrones detrás de abstracciones para que el poder elegir una fecha sea tan simple como $element.datePicker().

React no es una excepción a esto. react-router es uno de los casos de éxito mejor conocido. En lugar de tener que implementar la lógica para cambiar de páginas en una single-page-app, alguien creó esta librería para que nosotros podamos usarla.

En este post compartiré tres patrones elementales que usamos en el desarrollo de componentes. El poder de cada uno de estos patrones es sutil, pero combinándolos una y otra vez, lograremos componentes muy poderosos, simples, y altamente reutilizables.

1. Atributos de propagación

Hay veces que tenemos un objeto y queremos pasar los valores de ese objeto como props a un componente. Esto es posible usando el operador en un objeto para propagar sus valores como props a un componente.

functionButton (props) {
	return<buttontype=”button”className=”btn” {...props} />;
}

Parece redundante utilizar el componente Button en lugar de button pero logra un feature importante: todos nuestros Button son de tipo “button” y usan la clase btn y así comparte su aspecto visual. Usando ... logramos pasar todos los demás props a button.

Hay veces que queremos pasar todos los props, excepto algunos. Para eso usamos la misma técnica, dividida en dos pasos.

functionButton (props) {
	const { primary, ...rest } = props;
	const className = primary ? ‘btn btn-primary’ : ‘btn’;
	return<buttontype=”button”className={className} {...rest} />;
}

En la primera línea de la función, le hacemos destructuring a los props: guardamos primary y los demás los “empaquetamos” dentro del objeto rest. En este caso usamos primary para cambiar la clase del botón.

En este ejemplo los atributos de propagación nos permitieron lograr dos cosas: estandarizar el tipo de botón y estandarizar su aspecto visual, manteniendo flexibilidad respecto a los demás props.

2. Composición

Nuestro Button es un buen botón. No hace mucho, pero lo que hace, lo hace bien. Sería una pena perder esas características. Pero la realidad es que este es solo el comienzo del desarrollo de los botones de la aplicación. El diseño nos pide que tengamos un tipo de botón que muestre un estado de loading cuando sea necesario. Tenemos dos opciones: expandimos la interfaz del botón para añadir esto que nos pide, o hacemos un nuevo botón.

Es probable que Button ya esté en uso en otras partes de la aplicación. Estoy casi seguro que a mis compañeras dev no les va a gustar que le cambie la interfaz a algo que ya ellas usan. Así que, debo evitar la primera opción a toda costa. Por suerte, hay una segunda opción totalmente viable y, como verás, es una mejor alternativa. Para esto usaremos el patrón de composición.

La composición, en pocas palabras, trata de crear un nuevo componente que internamente usa otros componentes para aprovechar las habilidades que ya tiene ese componente. Veamos un ejemplo.

functionLoadingButton (props) {
	const { loading, disabled, children, ...rest } = props;
	return (
		<Buttondisabled={disabled || loading} {...rest}>
			{children}
			{loading && <LoadingIndicator />}
		</<span class="hljs-name">Button>
	)
}

¡Que simple quedó! Veamos cómo hubiera sido la primera opción.

functionButton (props) {
	const { primary, loading, disabled, children, ...rest } = props;
	const className = primary ? ‘btn btn-primary’ : ‘btn’;
	return (
		<buttontype=”button”disabled={disabled || loading}
			className={className}
			{...rest}
		>
			{children}
			{loading && <LoadingIndicator />}
		</<span class="hljs-name">button>
	);
}

Aunque inicialmente esto no se ve tan mal, puede crecer a ser un monstruo muy complejo. Considera un proyecto donde hayan 5 desarrolladores. Potencialmente cada quien en algún momento va a necesitar algo nuevo del botón. Si cada uno hace lo que quiere, inocentemente se comenzaría a crear una interfaz compleja para Button. Poco a poco va a ser más difícil modificar el componente.

Con composición hemos aprovechado toda la funcionalidad de Button, sin entrometernos en sus detalles.

Con esto no te quiero decir que nunca debemos modificar el código de Button una vez ya esté implementado. Aunque hay muchas veces que sí. Pero en este caso particular, solo debe modificarse cuando queremos implementar un cambio que deba afectar a TODOS los botones. Como el indicador de loading es una propiedad que solo debe tener algunos botones, prefiero desarrollar un nuevo componente en lugar de meterme a modificar Button.

Este patrón va mucho más allá que el diseño de componentes. Se considera un canon de buena práctica en el desarrollo de software.

3. Componentes Contenedores y Componentes de Presentación

El equipo de marketing quiere entender qué acciones están tomando los usuarios en la página. En otras palabras, le quieren hacer tracking a los clicks en los botones. Inicialmente parecía que solo iban a ser dos o tres botones, pero resulta que su imaginación tomó vuelo y ahora quieren hacerle tracking a casi todo. Tendremos que modificar Button para que cualquier botón en la app pueda ser analizado.

En este caso estamos introduciendo lógica al botón. Está lógica va mucho más allá que la lógica que se usa para presentar el botón; implica cambios en el comportamiento del botón. En pseudocódigo sería algo así:

Cuando ocurra un click, si props.eventName existe, haz una petición a Google Analytics. Inmediatamente, si props.onClick existe, llámalo con el evento del click como su primer y único argumento.

Aunque todo esto lo podemos hacer dentro de Button, lo estaríamos cargando con demasiadas responsabilidades. Además de darle el estilo y aspecto visual, también se tiene que encargar de qué pasa cuando se le hace click. Cuando un componente empieza a tener responsabilidades más allá de su presentación es buena oportunidad para crearle un contenedor que se encargue de todo lo que no es su presentación visual.

// Button.jsclassButtonContainerextendsReact.Component{
	handleClick = event => {
		if (this.props.eventName) {
			window.ga(‘event’, this.props.eventName);
		}

		if (this.props.onClick) {
			this.props.onClick(event);
		}
	}

	render() {
		// Queremos pasar todos los props, excepto onClick y eventName. El click lo estaremos manejando en el método this.handleClick.const { onClick, eventName, ...rest } = this.props;

		return (
			<ButtononClick={this.handleClick} {...rest} />
		);
	}
}

function Button (props) {
// Button se queda igual
	// ...
}

export default ButtonContainer;

Et voila! Todo lo que concierne a la presentación del botón en la interfaz se quedó igual. De hecho, no tocamos ese código en lo absoluto. Lo que hicimos fue usar un contenedor cuya responsabilidad es manejar y controlar el comportamiento del botón.

¿Te diste cuenta que este patrón también toma prestado del patrón de composición? Esto sucede de vez en cuando y es normal. De hecho, tomando prestado un poco de muchos patrones es como surgen nuevos patrones. Y con nuevos patrones es que la ingeniería de software da un paso adelante en su desarrollo. Te invito a que utilices estos patrones en tus aplicaciones. Quizás al principio no sea obvio cómo usarlos, pero no te desesperes. Mientras más código escribas, más fácil va a ser identificar dónde y cómo usarlos. Y el día que menos te lo esperes, inventas uno.

Si tienes preguntas, no dudes hacerlas en los comentarios. Y cuando descubras un nuevo patrón, compártelo conmigo por Twitter @sparragus.

¡Happy hacking!

Richard B.
Richard B.
richard

2351Puntos

hace 5 años

Todas sus entradas
Escribe tu comentario
+ 2
3
11250Puntos

Por eso para mi, cuando se inicia un proyecto que quiera crecer, es indispensableutilizarhttps://github.com/storybooks/storybook esto permite tener de manera organizada estos patrones