Artículo por: Alberto Rivera Cristian Castillo
React desde 0 | Conceptos base y fundamentos de React [1]
¡Hola MiniCoder! Con este nuevo artículo vamos a dar comienzo al curso de React desde cero con TypeScript 🔥 Aunque antes de trabajar con esta librería (library en inglés, que sabemos que no es exactamente lo mismo pero normalmente se le llama así) vamos a repasar algunos conceptos y a explicar qué es React y qué bondades nos ofrece.
¿Qué es React?
React es una librería que esta basada en la programación orientada a componentes en la que cada componente es una pieza con la que un usuario puede interactuar. Estas piezas o componentes se crean usando JSX y nos permite escribir HTML dentro de código Javascript.
Estos componentes son reutilizables y se combinan para crear componentes mayores hasta completar la lógica de una aplicación web. Esta es la forma de tener HTML dinámico con toda la funcionalidad de JavaScript, y con su estilo gráfico de CSS centralizado y listo para ser abstraído y usado en cualquier otro proyecto.
Particularidades de React
Algunos de los aspectos más importantes que debemos mencionar de React son los siguientes:
Los componentes de React, de forma general, se definen por medio de funciones que devuelven código JSX.
Las actualizaciones en React son asíncronas → si yo cambio un dato del estado en un componente de React no veré ese cambio realizado hasta que el componente sea actualizado en el DOM.
El estado en React es inmutable, es decir, los datos que manejamos en nuestra aplicación no pueden ser mutados directamente.
Con esto se busca desarrollar aplicaciones robustas y bajo un buen rendimiento. Esto supone una nueva forma de pensar y por tanto nos llevará un tiempo aprenderlo.
Uno de los los errores que se suelen cometer cuando empezamos con React, es aplicar mentalidad Java, .net o Angular, esto no funciona ni es recomendable.
React se trabaja mejor con una estructura Funcional y Declarativa, y con introducción de Hooks en la versión 16.8.0 de React, hemos pasado a desarrollar con una aproximación realmente funcional. Esto quiere decir:
- Los componentes de tipo clase se han quedado como Legacy y no es recomendable trabajar con ellos de forma asidua.
- Podemos tener estado en componentes funcionales vía Hooks, que veremos en los próximos artículos.
- Olvidarnos de la herencia, ahora usaremos la composición para crear componentes más complejos y reutilizar lógica.
- Es un cambio de paradigma para los que vienen de tecnologías como .net, java...
Vamos a ver estos conceptos desgranados y salpicados con fragmentos de código para entender mejor de qué estamos hablando 🦄.
Diferencias entre librería y Framework
El primer tema aclarar es si React
es una Librería o un Framework. Para ello vamos a aclarar la diferencia entre Librería y Framework.
¿Qué es una librería
? Una librería es una herramienta que soluciona un problema en tu código y cubre un problema que buscas solucionar. Si se da este caso, podemos decir que tu código usa una librería.
¿Qué es un framework
? Es algo que intenta darte una solución completa a un problema y te marca la manera en la que tienes que trabajar, es decir, tu código se desarrolla alrededor de un framework, tu no has creado el framework, pero tu código está fuertemente atado a su uso (no tiene validez fuera de el).
Por lo tanto React es una librería que te soluciona la creación de componentes en la parte de usuario, para todo lo demás puedes usar lo que quieras: ES6, ES7, TypeScript... La ventaja de esto es que tu código no depende 100% de React y su desventaja es que tienes que elegir bien el resto de opciones para completar tu puzzle sin atarte en exceso a la solución que desarrolles.
La inmutabilidad en React
Cuando trabajemos en React, pocas veces modificaremos un objeto o array directamente, si necesitamos hacer una modificación crearemos un objeto nuevo. Por ejemplo, piensa que tenemos la ficha developer → id, name, surname
, en caso de querer modificar el nombre creo un developer
nuevo copiando todas las propiedades anteriores y sobreescribo la propiedad nombre. Esto nos aporta:
- Un objeto se puede usar en varios sitios, si lo mutamos, estamos cambiando sin avisar ¿Te imaginas que mutamos por error un campo price y afecta toda la aplicación?
- Si muto un objeto de datos asociado a un componente ¿Cómo sé que tengo que repintar el componente? ¿Comparando una a una sus propiedades y subpropiedades?
- Si mis objetos son inmutables mi aplicación es predecible, ya para saber si ha habido alguna modificación sólo tengo que comparar apuntando a la dirección de memoria de la propiedad del objeto. Hace todo el proceso de comprobación más sencillo.
- Trabajar con inmutabilidad no es fácil, podemos tener ayudas con las librerías immer o deepfreeze, o ahora de manera nativa usar
Object.freeze()
. Aunque esto no será siempre necesario, ya que con buenas prácticas programaremos de forma inmutable casi sin darnos cuenta.
* Flujos unidireccionales [Realizar nuestras propias capturas]
El paso de datos fluye en un solo sentido. React está basado en el paso unidireccional de los datos, podríamos decir que van de arriba hacia abajo. Los datos de mi aplicación fluyen del componente padre a los componentes hijos, como si fuesen un árbol.
Si tengo un estado por medio de useState
, ese estado no se puede modificar directamente desde el componente, se realiza en cambio una petición al estado para poder actualizarlo por medio de su función setState
, y el motor de React decide cuando realizar esa actualización y comunicárselo a los componentes.
Además las propiedades que recibe un componente hijo por parte de su componente padre es de solo lectura, por lo tanto para modificar estados a un nivel superior tengo que usar callbacks.
Vamos a verlo con un ejemplo:
Nosotros tenemos el componente padre con toda la info que le va pasando a los hijos fragmentada, si desde un hijo queremos cambiar por ejemplo la cantidad de productos en el detalle tiene que burbujear para que sea el padre el que la modifique. En un futuro veremos que existen otras técnicas para no provocar el bubbleBackHell o exceso de callbacks enlazados entre si para cambiar un valor de un componente superior.
Una vez que tenemos esto claro podemos pasar a trabajar en React y explicar un poco la composición y algunas particularidades en base al código 🧙♂️.
Componentes de React - JSX
JSX es una extensión de JavaScript
creada para la librería React que nos permite utilizar JS
en nuestro HTML
convirtiendo etiquetas en elementos reactivos. Su nombre significa JavaScript Syntax Extension
y se le llama también JavaScript XML
.
Esta extensión nos permite escribir un código más limpio, con pocos factores y sin tantas repeticiones. En estos dos ejemplos se puede ver la simplificación del uso de JSX a la hora de programar aplicaciones en React:
// Componente de React sin JSX var MiniCodeComponent = function MiniCodeComponent(){ return _react.default.createElement("div", null, _react.default.createElement("h2", null), "Hello from React"), _react.default.createElement("h3", null, "Mini Code Lab:)") ); } // Componente de React con JSX import React from "react" export const MiniCodeComponent = () => ( <div> <h2>Hello from React</h2> <h3>Mini Code Lab :)</h3> </div> )
Pero ¿cómo funcionan las expresiones JSX? En el siguiente ejemplo vamos a declarar una variable llamada hero
que utilizaremos mediante el uso de llaves.
const hero = 'Spider-Man'; const greetings = <h1>Yo soy {hero}</h1>;
Dentro de las llaves
de JSX se puede escribir cualquier expresión de JavaScript.
const carWheels = <p>Cars have {2 + 2} wheels</p>;
Hasta ahora hemos visto ejemplos sencillos que contienen una sola etiqueta de HTML, pero JSX nos permite insertar un bloque de mayor tamaño de HTML.
Para esto hay que tener en cuenta que todo el código HTML que sobrepase un único elemento deberá ir anidado entre paréntesis que incluyan un elemento padre, como el div
en el siguiente ejemplo:
const myHeader = ( <div> <h1>This is the title</h1> <h2>This is the subtitle</h2> <p>This is some text</p> </div> );
Al ser HTML una parte fundamental de esta sintaxis también podremos añadirle clases
a las etiquetas que contenga nuestro código para añadir estilos. La única diferencia en JSX es que utiliza el término className
en lugar de class
:
const myName = <h2 className="heroname">Luke Skywalker</h2>;
Los condicionales también tienen cabida en React, pero no se pueden declarar dentro de nuestro elemento JSX. Para poder hacer uso de condicionales tendremos que colocarlos fuera del elemento y llamarlos con las llaves anteriormente mencionadas:
const x = 10; const time = <p>{x < 12 ? 'Good morning' : 'Good afternoon'}</p>;
Componentes de React
Los componentes en React son funciones, aunque debemos recordar que los Class Components existen, aunque podemos considerarlos legacy.
import React from 'react'; export const MiniCodeComponent = (props) => { return <h2> Hello Mini Coders {props.name}!</h2>; };
Particularidades de los componentes de React:
- Son funciones que se ejecutan y mueren una y otra vez en cada render.
- Devuelven siempre React Elements (JSX).
- Me permite dividir el UI (interfaz de usuario) en piezas independientes y reusables.
- Los componentes son piezas aisladas.
- Puedo hacer composición de componentes.
- Exponen un “contrato” (props) a través del cual reciben información.
Ciclo de Vida de un Componente
Todo suele seguir un ciclo, por ejemplo, los humanos nacemos, crecemos y morimos. Los componentes de React también se rigen por un ciclo parecido.
Los componentes se crean (se montan en el DOM), se actualizan ("crecen"), y mueren (se liberan del DOM). A esto lo denominamos ciclo de vida de un componente.
Propiedades (Props) e interacción entre componentes
Las propiedades definen un “contrato” que permite que otros componentes puedan comunicarse con un componente que hayamos creado e interactuar con él. Es decir, podemos tener un componente dentro de otro y comunicarlos entre sí a través de estos props
.
import React from 'react'; interface Props { name: string; } export const HelloMiniCoder = (props: Props) => { return <h2> Hello {props.name} </h2>; };
Detalles que debemos que tener en cuenta con respecto a las propiedades props
:
- Siguen el flujo direccional de componente padre a hijo.
- Son de solo lectura y no podemos mutarlas.
- La definición de las propiedades se lleva muy bien con TypeScript, y el uso de éste nos aportará una gran ventaja.
- ¿Cómo informo de hijo a padre para cambiar los valores de los props? Pues pasando propiedades de tipo función que se comporten como callbacks.
Propiedades y callbacks
Es la manera que tenemos de informar de hijo a padre sobre la necesidad de modificar una prop. El hijo no tiene poder de modificar la información recibida sino que solicita un cambio al componente que se la envía a través de un callback.
import React from 'react'; interface Props { name: string; onChangeName: (newName: string) => void; } export const NameEdit = (props: Props) => { return <input value={props.name} onChange={(e) => props.onChangeName(e.target.value)} />; };
Tenemos un componente NameEdit
que recibe un nombre y tiene un input para que el usuario pueda cambiarlo, ¿cómo puedo actualizar el nombre si es solo de lectura? Pues podemos definir un parámetro de tipo function onChangeName
que será un callback que invocaremos en el onChange
del input cada vez que cambie su valor, esto burbujea hacia el padre y este es el que realmente cambiará el valor.
Por lo tanto, tenemos que tener en cuenta dentro de la comunicación entre componentes que:
- Tengo como propiedad un callback (función).
- En cada cambio del input invoco dicha función.
- La función se ejecuta en el componente padre.
- El componente padre tiene control de cuando se ejecuta.
- Cuando el componente padre cambie su información, se reenviará al componente hijo.
Componentes Funcionales
import React from 'react'; export const MyDeveloperComponent = (props) => { const [developerName, setDeveloperName] = React.useState('Alberto'); return ( <> <h4>{developerName}</h4> <input value={developerName} onChange={(e) => setDeveloperName(e.target.value)} /> </> ); };
Vamos a diseccionar este componente:
- Tenemos una función como la definición del componente, que una vez ejecutada devuelve un valor JSX y se destruye.
- Un componente de tipo función siempre devuelve un fragmento de JSX. En nuestro caso el
h4
y elinput
. - Aquí tenemos un título
h4
que contiene el nombre de la constantedeveloperName
y uninput
cuya funcionalidad es cambiar el valor de esta constante cuando escriben nuestro usuarios. - A esa constante que cambia de valor la denominamos
state
y la hemos creado mediante un hook. - Si se crea y destruye...¿Cómo tenemos persistencia? ¿Cómo emulamos las variables miembros de los componentes de clase? Esto lo veremos con los Hooks que ha desarrollado el equipo de React, aunque aquí has podido ver un ejemplo con
useState
.
Estado - lectura y escritura de información
Este Hook lo veremos en profundidad más adelante pero queremos que tengáis una pequeña intro para poder trabajar con él sin entrar demasiado en sus particularidades.
Los estados nos permite almacenar información de lectura y escritura. Una de las cosas que no cuadran cuando usamos componentes funcionales es que una función se ejecuta y muere, es decir todas las variables con datos almacenados se pierden, y esto ocurre cada vez que renderizamos el componente. ¿Cómo podemos hacer que nuestros datos no desaparezcan?
import React from 'react'; export const HelloMiniCoder = () => { const [name, setName] = React.useState('Alberto'); return <input value={name} onChange={(e) => setName(e.target.value)} />; };
Primero invocamos a la función useState
que nos permite indicarle el valor inicial que queremos almacenar: 'Alberto'
.
Este useState
devuelve un array con dos elementos name
y setName
, siendo name
el valor recuperado del estado y setName
una función que me permite modificar dicho estado (Lo podemos ver como un getter y un setter).
Este es nuestro primer Hook, un concepto con el que nos tendremos que familiarizar más adelante. Pero tenemos que tener en cuenta:
useState
me permite persistir datos más allá de la muerte de la función.- La asignación de valor es asíncrona.
useState
me permite almacenar tantos tipos simples como objetos.- Si manejo estructuras inmutables en el estado, se pueden hacer optimizaciones.
useState
se basa en la tecnología de Hooks.- Antes de usar Hooks hay que saber bien como funcionan.
Manejo de la Asincronía con UseEffect
En React podemos pedir que se actualice el estado de nuestro componente, pero es el propio motor de React el que decide cuando aplicar esos cambios. Es decir, las actualizaciones de nuestro estado en React son asíncronas, vamos a verlo con un ejemplo:
import React from 'react'; export const MyDeveloperComponent = (props) => { const [developerName, setDeveloperName] = React.useState('Alberto'); // Presta atención a esta función: React.useEffect(() => { console.log(developerName); //Alberto setDeveloperName('Cristian'); console.log(developerName); //Alberto }, []); return ( <> <h4>{developerName}</h4> <input value={developerName} onChange={(e) => setDeveloperName(e.target.value)} /> </> ); };
Si observamos lo que hace este componente parecido al que hemos visto antes, cuya funcionalidad es renderizar el nombre de un developer, podemos sacar las siguientes conclusiones:
- Tenemos un
useState
que devuelve un array del que destructuramos un valor (el nombre del developer) y una función (que nos ayuda a modificar nuestro estado). - En el
useEffect
estamos cambiado el nombre de Alberto por el de Cristian. Esto sucede tras montarse el componente en el DOM. - Si nos fijamos bien, pese cambiar el nombre seguiremos haciendo un log que dice Alberto dentro de la ejecución de la función de
useEffect
. Esto puede añadir complejidad a nuestros desarrollos si no lo comprendemos bien. Aunque debemos tener en cuenta que una aplicación suele ser asíncrona (por ejemplo, con llamadas a un servidor) por lo que la obtención de este dato no es inmediata. - React optimiza el renderizado de nuestra aplicación / componentes. Y con renderizado nos referimos al acto que lleva a React ejecutar las funciones internas y repintar un componente.
- Podemos agrupar varios
setState
en un solo render. Siempre con cuidado y atomizando todo el contenido lo máximo dentro de nuestras capacidades.
Componetización en React
Si rompemos un component grande en subcomponentes, tendremos código más legible (principio de mismo nivel de abstracción) y potencialmente más reusable. Si te tienes que enfrentar a un proyecto en React te aconsejamos atacarlo desde un punto de vista de Desarrollo Progresivo, es decir:
- Plantea un layout y luego ve componetizando.
- Añade al contenedor datos mock.
- Sustituye los datos mock por aquellos de una API real.
- Usa un Context para que tus componentes sean reactivos a dicho contexto
Cómo creamos un proyecto en React
Tenemos dos opciones o vías para crear un proyecto de React:
Ventajas de usar el CLI de CRA (create-react-app):
- Rapidez de inicio.
- Ya está preconfigurado y no tienes que preocuparte de webpack.
Desventajas:
- El CLI funciona como algo mágico y si algún día necesitas meter mano a tu proyecto será complicado. Para ello acabaras haciendo un
eject
para sacar tu proyecto y añadir tu propia configuración
Ventajas desde cero:
- Control absoluto sobre tu proyecto.
Desventaja:
- Necesidad de conocer webpack y actualizar en caso que existan nuevas versiones (muy habitual).
- Configurar y actualizar siempre las dependencias de React manualmente, con CRA tendremos esto mucho más fácil.
Trabajando con el CLI de React
En este ejemplo vamos a crear un proyecto desde cero usando el CLI del equipo de Facebook. Ahora podemos descargar y preparar un proyecto con la última versión de React haciendo uso de npx
, npm package runner, que descarga una copia temporal de create-react-app y lo ejecuta.
Antes deberíamos tener cuidado si ya hemos instalado alguna vez de manera global create-react-app
, es decir:
npm install create-react-app -g
Por lo tanto si quieres hacer uso de npx
, recuerda desinstalarlo:
npm unistall create-react-app -g
Ahora podemos crear nuestro primer proyecto de React, vamos a ejecutarlo:
npx create-react-app example_zero
Y es más si queremos usar una plantilla de Typescript podemos ejecutarlo así:
npx create-react-app example_zero --template typescript
Una vez termina de ejecutar debería aparecer algo así en tu terminal:
Y nuestro proyecto se vería similar a esto:
Si te fijas bien, no vas a poder encontrar referencia alguna a webpack así de primera vista. Para ello tendríamos que realizar el eject
aunque esto no te lo recomendamos por ahora, lo haremos por mera curiosidad en el taller en directo:
# Usar únicamente en el caso que explicamos, mucho cuidado ⚠️ npm run eject
Ten cuidado ya que una vez lo ejecutamos no hay vuelta atrás, esto solo lo deberíamos ejecutar si no estamos satisfechos con la configuración inicial del proyecto o necesitamos una configuración adicional que CRA no puede abarcar.
Y ahora viene la gran pregunta, ¿Cómo funciona React?
Funcionamiento de React
El primer punto en el que nos vamos a detener es en la carpeta public
y en concreto en el index.html
, ya que este es nuestro fichero de entrada donde se inyectará o se añadirán nuestros componentes de React.
Si ahora nos vamos a la carpeta src
veremos que en el index.tsx
tenemos a ReactDOM.render añadiendo nuestro componente de entrada <App />
y usando el document.getElementById('root')
lo enlaza con nuestro index.html
:
Por último vamos a nuestro fichero App.tsx
en el que encontraremos nuestro componente principal de React:
Este es el flujo que tiene React y sobre el que nosotros iremos trabajando poco a poco. Pero antes vamos a levantar nuestro proyecto para verificar que todo funciona correctamente:
npm run start
En la terminal veremos lo siguiente:
Y en nuestro navegador:
Con todo esto, estamos más que preparados para arrancar con React y aprender como nunca 🚀. Sabemos que ahora mismo ante tanta bomba informativa 💣 estarás buscando un sentido claro a todo esto, pero son conceptos en los que trabajaremos e iremos viendo transversalmente durante los próximos post, gracias a todo esto entenderemos React en profundidad en el futuro.
Muchas gracias por tu tiempo como siempre, ¡vamos a por ello MiniCoder! 💥 💥 💥