ReactJS Training: Entendiendo React y TypeScript
If you want to read the English version of this post, click here.
En este ejercicio, presentaremos las dos librerias principales que utilizaremos para desarrollar aplicaciones web: React y TypeScript. Para esto, veremos ejemplos pequeños pero concretos que nos ayudarán a comprender los conceptos principales, de a uno a la vez.
Pero antes de comenzar a codear, hay que entender lo que vamos a hacer. Pensa en el diseño de alguna interfaz de usuario (UI), la más común que hayas visto y preguntate:
- Si tengo que hacer esto, ¿por dónde deberia empezar? ¿Tengo que hacer todo de cero? ¿Hay algo por ahí que pueda usar?
- ¿Cómo puedo dividir el trabajo en diferentes tareas? ¿Cuáles son las más importantes?
- ¿Es posible reutilizar lo que voy a hacer con otros proyectos a futuro?
En las siguientes secciones, trabajaremos juntos para proponer algunas respuestas a estas preguntas.
Introducción: Desarrollo web
Antes de comenzar a programar, hagamos un resumen rápido de los conceptos fundamentales del desarrollo web.
Si ya conoces estos conceptos, puedes omitir esta sección introductoria y saltar directamente a la siguiente sección.
- Las aplicaciones web son, básicamente, aplicaciones que pueden ejecutarse mediante un navegador (web) (Chrome, Firefox, Edge, Safari, Internet Explorer, etc.).
- Los navegadores web solo entienden XMLs (HTML en especifico), JavaScript y CSS.
- HTML proporciona la estructura básica de los sitios, también conocido como su markup.
- CSS se utiliza para controlar la presentación, el formato y el diseño, también conocidos como sus estilos.
- JavaScript se usa para cambiar el comportamiento de diferentes elementos HTML y estilos CSS dinámicamente. Por lo general, depende de la interacción del usuario (por ejemplo, un click de un botón, un toque del dedo o incluso cuando hablas).
Por ejemplo, cuando navegas al sitio de Google porque quiere buscar algo, ¡está utilizando una aplicación web!
En este caso:
- La estructura de la página se define utilizando HTML (el texto que se muestra como resultados, la sección derecha, etc.).
- Como esta presentada la estructura de la página se configura utilizando CSS (el color de la fuente, el diseño de los resultados y cómo éstos se muestran en la página, etc).
- La interacción del usuario se controla con código de JavaScript. Por ejemplo, lo que sucede cuando alguien escribe una pregunta y presiona ENTER se configura via código.
La cantidad de cosas que podemos hacer en cualquier aplicación web y su complejidad ha crecido con el tiempo. Afortunadamente, ahora podemos (re) usar librerias JavaScript para evitar reinventar la rueda y reducir el trabajo repetitivo. React y TypeScript son algunas de librerias reutilizables.
Section 1: What ReactJS is and what it is for
Nota: Si deseas obtener más información sobre React, visita su sitio oficial.
React es una librería de JavaScript para construir interfaces de usuario. Una de las mejores cosas es que cambia la forma en que generalmente pensamos al crear una aplicación. Propone dividir la lógica en partes, llamadas componentes, siguiendo el principio de responsabilidad única o SRP: cada pieza (componente) debe hacer solo una cosa.
Usando como ejemplo nuestro diseño anterior de interfaz de usuario, podemos dividir la página en (por ejemplo) los siguientes componentes:
- Un TableComponent, para mostrar todas nuestras entidades.
- SearchComponent, para filtrar las entidades que mostramos.
- DetailsComponent, para mostrar información relacionada con una entidad seleccionada.
- SidebarPanelComponent, para encapsular la lógica de lo que representamos en el panel izquierdo (los enlaces y el botón PROD).
- …y así
La forma en que dividimos nuestro diseño en componentes es completamente arbitraria y, aunque parezca difícil de hacerlo al comienzo, será más fácil con el tiempo. La prueba y error nos dirá, con el tiempo, cuál es la mejor opción en términos de reutilización y mantenimiento.
Nota: Podemos continuar dividiendo los componentes que definimos previamente en componentes más pequeños, cada uno responsable de hacer una sola tarea. Por ejemplo, ¿Podrias proponer cómo dividir el componente TableComponent en piezas más pequeñas?
React en accion
React es una libreria que nos ayudará a construir nuestra interfaz dividiéndola en componentes. Vamos a averiguar juntos lo que significa esto utilizando los ejemplos de la documentación de React:
Abra el ejemplo Hello React! haciendo click aqui.
import React from "react";
import ReactDOM from "react-dom";const App = () => <h1>Hello React!</h1>;
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Analice el codigo durante unos minutos. Observa que el componente <App/>
es una React Function que devuelve markup con sintaxis JSX.
Que es JSX? JSX es la extensión de facto de sintaxis para representar elementos HTML en el ecosistema React. No es HTML, pero es bastante similar. Puede recordarte a un lenguaje tipo template, pero además viene con todo el poder de JavaScript. Si desea obtener más información sobre JSX, puedes consultar aquí.
Un browser no entiende JSX. Para que nuestro código funcione en los navegadores, necesitamos compilar nuestro código a JavaScript, que los navegadores puedan entender. No tenemos que preocuparnos por esto por ahora, pero el resultado final de este código es (y siempre será) JavaScript.
var App = () => React.createElement("h1", null, "Hello React!")
var rootElement = document.getElementById("root");
ReactDOM.render(React.createElement(App, null), rootElement);
Ahora modificaremos este ejemplo para proporcionar al componente <App/>
la capacidad de personalizar el mensaje presentado en la UI. Para ello:
Reemplace el mensaje hardcodeado con un parámetro message
que vamos enviar al componente:
const App = ({ message }) => <h1>{message}</h1>;
Ahora defina un mensaje customizado al inicializar el componente:
ReactDOM.render(<App message=”Hello React!!” />, rootElement);
Las funciones de React pueden recibir arbitrariamente entradas de sólo lectura llamadas props (abreviatura de properties). Estas props, modelan lo que devuelve la función. En nuestro ejemplo anterior, el mensaje que enviamos en el navegador depende de la propiedad message
.
Nota: Como regla general, todas las funciones y componentes de React deben actuar como “funciones puras” con respecto a sus props. Lo que significa que lo que devuelven al navegador está determinado por sus valores de entrada “sin mutar los parámetros/argumentos recibidos” (también conocido como “side effect”).
Sigamos con otro ejemplo. Abra el ejemplo React Timer haciendo click aqui.
class Timer extends React.Component {
...
}const App = () => <Timer />;
const rootElement = document.getElementById("root");
ReactDOM.render(App, rootElement);
Ahora tenemos dos archivos: un index.jsx que inicializa nuestra aplicacion y el archivo Timer.jsx que define un componente React. Esta vez, nuestro componente es una clase que extiende React.Component
, tiene un metodo constructor()
y otros cuatro métodos más.
export default class Timer extends React.Component {
constructor(props) { ... }
tick() { ... }
componentDidMount() { ... }
componentWillUnmount() { ... } render() {
return <div>Seconds: {this.state.seconds}</div>;
}
}
React permite definir componentes como clases o funciones. Cuando se extiende a React.Component, el único requisito es definir un método render(), responsable de devolver los elementos JSX que se mostrarán en el navegador.
Además, un componente de clase React proporciona otros métodos integrados que se ejecutan en momentos específicos mientras tu aplicación esté corriendo, y uno puede hookear (asociar) su propio código propio a estos métodos de ciclo de vida. Algunos de estos métodos son el constructor ()
, componentWillMount ()
, componentWillUnmount ()
, etc.
Note: El metodo render() es equivalente la funcion React que usamos en el ejemplo anterior.
Por ejemplo, revisemos el código dentro de componentDidMount()
y componentWillUnmount()
.
export default class Timer extends React.Component {
... componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
} componentWillUnmount() {
clearInterval(this.interval);
} ...
}
Este código se encarga de inicializar, ejecutar y limpiar el temporizador. Y mediante el uso de estos dos métodos integrados, React garantiza ejecutar este código cuando el componente se dibuja en el DOM ("mounted") y cuando se elimina ("unmounted").
Nota Si queres saber mas sobre state y ciclos de vida de un componentes, podes ver mas informacion en la documentacion oficial.
Por último, analicemos el último método de la clase. El método tick()
actualiza el estado del componente llamando a this.setState()
:
export default class Timer extends React.Component {
... tick() {
this.setState(prevState => (
{ seconds: prevState.seconds + 1 }
));
} ...
}
Cada componente de React puede almacenar su estado local via this.state
. State es similar a las props, pero es privado y está completamente controlado por el componente. El estado NO DEBE modificarse directamente. En su lugar, siempre debe utilizar el método this.setState ()
y devolver un nuevo estado.
Nota: La razón por la que no se muta el estado directamente es que una actualización de estado podría potencialmente desencadenar una nueva ejecución del método
render ()
, redibujando el componente. Usandothis.setState()
React hará esta llamada junto con otros cálculos.
Llegamos al último ejemplo de esta sección. Abra el ejemplo Questionnaire haciendo click aquí.
class Questionnaire extends React.PureComponent {
...
}const App = () => <Questionnaire />;
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Tomate unos minutos para analizar todas las piezas de código involucradas y la responsabilidad de cada una de ellas. Juga con las casillas de verificación y revisa los resultados.
Abra los archivos Question.jsx y Answer.jsx. Observe que estos archivos modelan una función React (o un componente funcional React) que recibe diferentes props y devuelve un resultado, modelado a través de JSX.
export default ({ question, value }) => (
<p>
<span>{`${question}: ${value}`}</span>
</p>
);
Luego, abra el archivo Questionnaire.jsx. Mire los métodos utilizados al dibujar las diferentes partes del componente: las preguntas y las respuestas. Observe cómo organizamos toda la lógica de dibujo utilizando el método render()
.
export default class Questionnaire extends React.PureComponent {
...
renderQuestions() {
...
} renderAnswers() {
...
} render() {
return (
<React.Fragment>
{this.renderQuestions()}
{this.renderAnswers()}
</React.Fragment>
);
}
}
Nota: React Fragment es un “componente incorporado” (built-in) que se usa para agrupar (lógicamente) una lista de elementos secundarios sin agregar nodos adicionales al DOM. Si desea obtener más información al respecto, haga clic en aquí.
Concentrémonos en el método renderQuestions()
y localicemos la prop onChange
. Pasamos a esta prop el método this.handleQuestionChanged()
que actualizará la UI si se responde la pregunta. Y almacenará el valor en el estado del componente Questionnaire.
export default class Questionnaire extends React.PureComponent {
... renderQuestions() {
...
return questions.map(question => (
<Question
key={question.id}
id={question.id}
text={question.text}
checked={question.value}
onChange={this.handleQuestionChanged}
/>
));
} ...
}
Con React, manejamos la interaccion del usuario a través de _”eventos”_ de una manera similar al si sólo utilizáramos JavaScript y HTML:
- Todos los elementos JSX exponen un conjunto de eventos (ver todos los eventos compatibles aquí).
- Podemos hookearnos a cada evento adjuntándole una función. De manera predeterminada, recibirá un objeto SyntheticEvent como primer argumento, pero se puede cambiar.
Resumiendo
Al revisar estos ejemplos, aprendimos lo siguiente:
- En React, usualmente usamos JSX para describir cómo debería ser la UI.
- Para personalizar lo que generamos, podemos enviar props a nuestras funciones o componentes React.
- React proporciona la clase React.Component para ayudar a encapsular nuestro código en componentes.
- La salida de un método render() de un React.Component le dice al navegador lo que queremos dibujar.
- Cada componente React podría tener su propio state para almacenar valores localmente.
- Puede capturar las interacciones del usuario hookeando una función o método de clase a un evento JSX .
Nota: Puedes encontrar un set completo de ejemplos, cada uno centrado en un sólo concepto de React, en la documentación oficial.
Seccion 2: Qué agrega TypeScript a la mezcla
TypeScript es una libreria de código abierto desarrollada y mantenida por Microsoft para implementar aplicaciones a gran escala en entornos JavaScript. Se compila a JavaScript simple, que se ejecuta en cualquier navegador, en Node.js o en cualquier motor de JavaScript que admita ECMAScript 3 (o más reciente).
En esencia, es un superconjunto sintáctico estricto de JavaScript que agrega tipado estático opcional al lenguaje en el momento del desarrollo.
Los principales beneficios de usar esta libreria son:
- Cuenta con verificación estática y refactorización de código y un amplio conjunto de herramientas y prácticas de desarrollo altamente productivas.
- Permite escribir código utilizando las últimas características y sintaxis de javascript, sin preocuparse por el soporte del navegador (porque se compilará en JS simple).
- No obliga a usar TypeScript: se puede escribir JavaScript si lo desea.
- Microsoft y Google lo mantienen. Y Angular lo usa.
TypeScript en accion
Veamos cómo funciona TypeScript usando un ejemplo:
Abra el playgorund de TypeScript. Este playground usa las mismas herramientas que el IDE de VSCode usa para interpretar el código.
En el menú desplegable ubicado a la izquierda de la página, seleccione la opción Hello world. Tómese un tiempo para leer detenidamente el código y comprender qué es TypeScript y qué hace. Si quieres, continua con los otros ejemplos de la sección JavaScript Essentials.
Ahora abra el ejemplo Classes 101. Repasémoslo juntos:
- En el panel izquierdo, hay código TypeScript que define la clase
Vendor
y un método para recibir clientes en la tienda, identificado por su nombre. - En el panel derecho, puede visualizar el equivalente de JavaScript, que se generó al transpilar el código TypeScript a ES2017. Este es el código que el navegador entiende y puede ejecutar.
- De manera similar, hay un código TypeScript a la izquierda que define la clase
FoodTruck
, y un método para recibir nuevos clientes.
Coloque el mouse sobre la instanciación FoodTruck
en la linea 47. Observe que el IDE muestra información sobre su constructor.
Agregue un segundo argumento para crear una instancia de FoodTruck
, por ejemplo ”asado”
. Abra la consola del desarrollador (o Developer tools) del navegador haciendo click derecho en la página (pero fuera del editor, por ejemplo en la barra de navegación azul en la parte superior). Luego, haz click en el botón Run.
¡Espera! Nuestro cambio no se muestra en la consola. Eso no es justo. Para que funcione, agregue console.log(nameOnlyTruck.greet());
en la linea 48 o reemplace la linea 54 con la instancia de nameOnlyTruck
. Y luego haz click de nuevo en el botón Run.
Nota: Cada navegador web moderno incluye un poderoso conjunto de herramientas para desarrolladores (Developer Tools) que ayudan a los desarrolladores a comprender lo que el navegador interpreta y encontrar posibles errores en el código (HTML, JavaScript o CSS). También le muestra métricas como el tiempo que tardó en cargar la página y cualquier otra solicitud que realizó (y está haciendo) el navegador. Si desea profundizar en esto, haga clic aquí.
Finalmente, reemplace la palabra (string) ”asado”
con el número 1
. Ahora verá un error en el código que le indica que “los números no están permitidos como argumentos del constructor”. Observe además que este mensaje se muestra en el panel de TypeScript y no en JavaScript (el del la derecha).
Solucione el problema reemplazando el número 1
con un valor de string (como "helado"
).
Y hemos terminado!
Resumiendo
Con este simple ejemplo, hemos aprendido que:
- El código en TypeScript se puede compilar a un código JavaScript que cualquier navegador puede ejecutar. Puede encontrar el manual de TypeScript aquí.
- TypeScript permite definir tipos de la misma manera que lo hace un lenguaje fuertemente tipado. Puede encontrar la especificación del lenguaje aquí.
- Todos los IDE modernos (VSCode, WebStorm, Atom, etc.) admiten TypeScript y pueden proporcionar sugerencias y mensajes de error (IntelliSense), así como análisis estático (a través de tslint).
🎉
Recuerde que puede encontrar el training completo en este repositorio de GitHub.