Skip to content

馃 Mi primer proyecto en REACT: Una completa introducci贸n a esta librer铆a

Notifications You must be signed in to change notification settings

amargopastor-codealong/ca-react-intro

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

5 Commits

Repository files navigation

Intro react

REACT: creado por Facebook.

REACT es una libreria de JS frontend (o framework* de frontend quiz谩s) de pintado por pantalla, similar a DOM manipulation, pero sin necesidad de recargar la pantalla. React se puede ejecutar tanto en el cliente como en el servidor. Adem谩s, podemos usar React Native para crear aplicaciones nativas para Android e iOS.

Conoceremos REACT en esta primera aproximaci贸n como una herramienta ideal para construir Single Page Aplications, las cuales ir谩n transformando su contenido sin necesidad de refrescar la propia p谩gina.

Todo esto lo har谩 REACT de manera autom谩tica mediante componentes: entidad visual de nuestra aplicaci贸n (ej: una lista, imagen con texto, interface de usuario, etc). React se emplea precisamente para facilitar la creaci贸n de componentes interactivos y reutilizables. Cada uno de estos componentes posee su propio estado. Cuando dicho estado cambia, React lo vuelve a renderizar.

REACT trabaja mediante el DOM en la sombra. Se trata de una herramienta que consensua el DOM del navegador y su propio DOM.

Podemos usar REACT para pintar en el navegador (react dom) y en apps (react native). En este aspecto, la forma interna de trabajar de REACT al pintar los componentes sirve tanto para navegador como para m贸vil.

En resumen: REACT es una librer铆a de frontend que sirve para visualizar y crear componentes interactivos y reutilizables.

Diferencia entre Shadow DOM y Virtual DOM

El Shadow DOM es una API del navegador que nos permite crear un 谩rbol de nodos DOM independiente dentro de un elemento del DOM. Esto nos permite crear componentes que no interfieran con el resto de la aplicaci贸n. Se usa especialmente con Web Components.

El Virtual DOM es una representaci贸n del DOM en memoria. Esta representaci贸n se crea cada vez que se produce un cambio en el DOM. Esto nos permite comparar el DOM actual con el DOM anterior y as铆 determinar qu茅 cambios se deben realizar en el DOM real. Lo usa React y otras bibliotecas para hacer el m铆nimo n煤mero de cambios en el DOM real.

React DOM

React DOM es la librer铆a que se encarga de renderizar los componentes de React para el navegador. Hay que tener en cuenta que React es una biblioteca que se puede usar en diferentes entornos (dispositivos m贸viles, apps de escritorio, terminal...).

Mientras que la biblioteca de React, a secas, es el motor de creaci贸n de componentes, hooks, sistema de props y estado... React DOM es la librer铆a que se encarga de renderizar los componentes de React espec铆ficamente en el navegador.

React Native, por ejemplo, har铆a lo mismo, pero para dispositivos m贸viles.

Set-up

Vamos a crear un primer proyecto en REACT que nos permita poner en pr谩ctica los conceptos iniciales. Para ello emplearemos NODEJS.

gitignore.io

touch .gitignore
yarn init -y
yarn add eslint
yarn eslint --init
yarn add react react-dom typescript axios parcel
tsc --init
yarn add @types/react @types/react-dom @types/node

package.json

{
	// (...)
	"scripts": {
		"start": "parcel index.html"
	},
	"dependencies": {
		// (...)
	}
	// (...)
}

.eslintrc.json

{
	"env": {
		"browser": true,
		"es2021": true
	},
	"extends": ["plugin:react/recommended", "airbnb"],
	"parser": "@typescript-eslint/parser",
	"parserOptions": {
		"ecmaFeatures": {
			"jsx": true
		},
		"ecmaVersion": "latest",
		"sourceType": "module"
	},
	"plugins": ["react", "@typescript-eslint"],
	"rules": {
		"react/function-component-definition": "off",
		"react/jsx-filename-extension": "off",
		"camelcase": "off",
		"import/no-unresolved": "off",
		"import/extensions": "off"
	}
}
touch index.html
mkdir src
cd src
touch App.tsx index.tsx

index.html

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>React App</title>
	</head>

	<body>
		<div id="app-root"></div>
		<script type="module" src="./src/index.tsx"></script>
	</body>
</html>

App.tsx

import React from 'react';

// Nota: Esto es un componente "funcional". Se le llama funcional porque se usa una funci贸n en lugar de una clase
const App = () => <p>Hola desde REACT</p>;

export default App;

index.tsx

import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

console.log('Starting react app...');

const appRoot = document.getElementById('app-root') as Element;
const root = createRoot(appRoot);

// Renderiza App (que es un componente) dentro del elemento appRoot. App es el COMPONENTE RA脥Z.
root.render(<App />);

Librer铆a react-dom

Un COMPONETE es un elemento compuesto de HTML y TS (elemento con interactivdad). Es decir, encapsulamos en una misma entidad visual una cierta funcionalidad; por ejemplo, la barra de b煤squeda de google es un componente con interactividad. De manera pr谩ctica un componente no es m谩s que una funci贸n que devuelve un html.

Componente RA脥Z: componente o entidad visual del que colgar谩n el resto de componente. Aqu铆 montamos toda la aplicaci贸n (todos los componentes). Los componentes mantienen una estructura jer谩rquica.

La nueva sintaxis TSX

REACT introduce una nueva sintaxis llamada TSX (TS + HTML): etiquetas html dentro de TS. TSX es una ampliaci贸n de la sintaxis de TS para soportar etiquetas html en sus documentos. Es una manera m谩s corta para programar en REACT.

Para este proyecto comenzamos a trabajar Parcel: una herramienta que lee los archivos y convierte las etiquetas TSX en sintaxis TS real gracias al transpilador (un codigo fuente a otro codigo fuente). Parcel usa este elemento para convertir TSX a TS y, posteriormente, a JS.

Dentro de la carpeta dist que nos genera parcel podemos encontrar nuestros archivos JS compilados y ver como se han transpilado los distintos elementos de nuestra aplicaci贸n.

Por ejemplo, el siguiente c贸digo no es c贸digo v谩lido de JS a menos que lo compilemos previamente (con parcel u otro compilador):

const App = () => <p>Hola</p>;

Cuando desarrollemos una aplicaic贸n completa con REACT veremos que existen distintas fases de compilaci贸n:

  • Compilar TSX a TS
  • Compilar TS al JS final

Parcel hace todo el proceso gracias a un programa interno llamado webpack: es d贸nde definimos el flujo de pluggins de conversi贸n a JS y sus distintas versiones.

La implementaci贸n de webpack es compleja por eso empleamos parcel que trae ya todo configurado.

Nuevo componente

cd src
mkdir components
touch Card.tsx

Editamos nuestro primer componente:

Card.tsx

import React from 'react';

const Card = () => <p>Card</p>;

export default Card;

Y lo incorporamos a la aplicaci贸n:

App.tsx

// (...)
import Card from './components/Card';

const App = () => (
	<div className="cardlist">
		<Card />
		<Card />
		<Card />
	</div>
);

// (...)

NOTA: Si a un div le queremos poner una etiqueta class en REACT empleamos className. La raz贸n por la que se llama className es porque class es una palabra reservada en JavaScript. Por eso, en JSX, tenemos que usar className para aplicar clases CSS.

REACT DEV TOOLS

Gracias a la extensi贸n de CHROME REACT DEV TOOLS podemos ver la gerarqu铆a y los componentes (diferenciados unos de otros) generados y pintados.

De esta manera en lugar de una estructura cl谩sica de DOM podemos ver cada componente y diferenciar los mismos (aunque un componente est茅 dentro de otro, esta herramienta los diferencia).

Vamosa detallar nuestro componente card:

Card.tsx

// (...)

const Card = () => (
	<div>
		<img
			alt="demo"
			src="https://preview.redd.it/0q9k35rs52461.jpg?width=1920&format=pjpg&auto=webp&s=28a96947814b6207c596c34b4e623bc6c9683692"
			width="300"
		/>
		<p>Developer Excuses</p>
	</div>
);

// (...)

Styles

A la hora de a帽adir estilos tenemos que enfrentarnos al siguiente reto: el CSS en REACT lo escribiremos como JS. En REACT, los estilos en l铆nea no funcionan como string, tienen una sintaxis especial llamada CSS como OBJECTO. La sintaxis correcta es creando un objeto ya que en REACT los estilos en l铆nea tienen que ser un objeto, no un string:

Card.tsx

// (...)

const styleObject = {
	border: '1px solid red',
	padding: 10,
	marginTop: 10,
	marginLeft: '5px',
	marginRight: 5,
	margin: '10px 5px 0 5px',
	display: 'inline-block',
	textAlign: 'center' as 'center',
};

const Card = () => {
	const image =
		'https://preview.redd.it/0q9k35rs52461.jpg?width=1920&format=pjpg&auto=webp&s=28a96947814b6207c596c34b4e623bc6c9683692';
	const title = 'Developer Excuses';
	return (
		<div style={styleObject}>
			<img alt="demo" src={image} width="300" />
			<p>{title}</p>
		</div>
	);
};

// (...)

ATENCI脫N: Convenci贸n de REACT llama al CSS como OBJ (com煤n para REACT, ANGULAR, REACT NATIVE...). En JS las propiedades no pueden tener guiones en su nombre por lo que no podemos declarar margin-top:10 sino emplear marginTop: 10.

Si ya estamos insertando variables podemos optimizar el proceso con objetos de informaci贸n:

mkdir data
touch excuses.tsx

excuses.ts

interface iExcuses {
	[index: string]: { title: string; image: string };
}

const list: iExcuses = {
	one: {
		title: 'I haven鈥檛 touched that module in weeks!',
		image:
			'https://preview.redd.it/0q9k35rs52461.jpg?width=1920&format=pjpg&auto=webp&s=28a96947814b6207c596c34b4e623bc6c9683692',
	},
	two: {
		title: 'It must be a hardware problem.',
		image:
			'https://preview.redd.it/0q9k35rs52461.jpg?width=1920&format=pjpg&auto=webp&s=28a96947814b6207c596c34b4e623bc6c9683692',
	},
	three: {
		title: 'Somebody must have changed my code.',
		image:
			'https://preview.redd.it/0q9k35rs52461.jpg?width=1920&format=pjpg&auto=webp&s=28a96947814b6207c596c34b4e623bc6c9683692',
	},
};

export default list;

Card.tsx

import React from 'react';
import list from '../../data/excuses';

const styleObject = {
	// (...)
};

export const Card = () => {
	const { title, image } = list.one;
	return (
		<div style={styleObject}>
			<img alt="demo" src={image} width="300" />
			<p>{title}</p>
		</div>
	);
};

PROPS

Al igual que las etiquetas tienen atributos, los componentes en REACT tienen propiedades o PROPS. De modo que los atributos se convierten en un objeto (datos) que contiene informaci贸n y nos permite trabajar, compartir informaci贸n entre componentes, etc.

Card.tsx

// (...)

const styleObject = {
	// (...)
};

const Card = (props: any) => {
	console.log('Excuse props', props);
	const { title, image } = list.one;
	return (
		<div style={styleObject}>
			<img alt="demo" src={image} width="300" />
			<p>{title}</p>
		</div>
	);
};

// (...)

Al hacer console.log('Excuse props', props); se muestran por consola 3 console props (por cada componente se ejecuta la funci贸n una vez). El mismo componente se est谩 reutilizando y pintando 3 veces. Esta es una de las ventajas de REACT: la reutilizaci贸n de componentes.

Las props son un objeto vac铆o que podemos emplear en nuestro favor. En la terminal vemos que cada componente recibe su objeto props (tiene un objeto con la etiqueta y valor indicados).

Lo que cada componente tiene como atributo en App.tsx se convierte en un objeto (con sus repectivos atributos) en el componente Card.tsx.

App.tsx

// (...)

const App = () => (
	<div className="cardlist">
		<Card ex="one" />
		<Card ex="two" />
		<Card ex="three" />
	</div>
);

// (...)

En REACT se suele escribir de la siguiente manera, sacando solo las props que nos interesan:

Card.tsx

// (...)

const Card = (props: { ex: string }) => {
	console.log('Excuse props', props);
	const { ex } = props;
	const { title, image } = list[ex];
	return (
		<div style={styleObject}>
			<img alt="demo" src={image} width="300" />
			<p>{title}</p>
		</div>
	);
};

// (...)

Explorando las props

Los componentes reciben props y devuelven la representaci贸n visual del componente. Podemos convertir styleObject en una funci贸n que recibe un par谩metro colorBorder y que la emple en los estilos:

App.tsx

export const App = () => {
	return (
		<div className="cardlist">
			<Card ex="one" color="red" />
			<Card ex="two" color="green" />
			<Card ex="three" color="yellow" />
		</div>
	);
};

Card.tsx

// (...)

const styleObject = (color: string) => ({
	border: `1px solid ${color}`,
	padding: 10,
	marginTop: 10,
	marginLeft: '5px',
	marginRight: 5,
	display: 'inline-block',
	textAlign: 'center' as 'center',
});

const Card = (props: { ex: string; color: string }) => {
	console.log('Excuse props', props);
	const { ex, color } = props;
	const { title, image } = list[ex];
	return (
		<div style={styleObject(color)}>
			<img alt="demo" src={image} width="300" />
			<p>{title}</p>
		</div>
	);
};

// (...)

Es decir, los propios estilos visuales se pueden parametrizar y escribir con poco c贸digo muchas cosas.

En resumen, un componente funcional recibe unas props y devuelve la representaci贸n visual de ese componente (que a su vez puede delegar la represnetaci贸n visual de otros componentes en los mismos).

Renderizado de listas

El renderizado de listas es la forma de iterar un array de elementos y renderizar elementos de React para cada uno de ellos.

Para REACT cada elemento es 煤nico, lo que le permite eliminar S脫LO el componente deseado identific谩ndolo mediante una key. En esencia, esto permite la reconciliaci贸n con el DOM.

REACT est谩 programado de tal manera que identifica mediante una key (un id, el iterador de map, etc) cada elemento de la lista y borra solo el indicado, haciendo que el performance en REACT sea muy r谩pido.

App.tsx

import React from 'react';
import list from '../data/excuses';
import Card from './components/Card';

const App = () => {
	const excusesKeys = Object.keys(list);
	console.log(excusesKeys);
	return (
		<div className="cardlist">
			{excusesKeys.map((ex, i) => (
				// eslint-disable-next-line react/no-array-index-key
				<Card key={i} ex={list[ex]} color="red" />
			))}
		</div>
	);
};

export default App;

Card.tsx

import React from 'react';

const styleObject = (color: string) => ({
	border: `1px solid ${color}`,
	padding: 10,
	marginTop: 10,
	marginLeft: '5px',
	marginRight: 5,
	display: 'inline-block',
	textAlign: 'center' as 'center',
});

const Card = (props: {
	ex: { title: string; image: string };
	color: string;
}) => {
	const { ex, color = 'red' } = props;
	console.log(ex);
	const { title, image } = ex;
	return (
		<div style={styleObject(color)}>
			<img alt="demo" src={image} width="300" />
			<p>{title}</p>
		</div>
	);
};

export default Card;

Hooks

Los Hooks son una API de React que nos permite tener el estado y otras caracter铆sticas de React de los componentes creados con una function. Hooks es gancho y, precisamente, lo que hacen, es que te permiten enganchar tus componentes funcionales a todas las caracter铆sticas que ofrece React.

Vamos a a帽adir un btn en cada card que ejecute una funci贸n excuseHandler:

Card.tsx

// (...)

const Card = (props: {
	ex: { title: string; image: string };
	color: string;
}) => {
	const { ex, color = 'red' } = props;
	const { title, image } = ex;
	const excuseHandler = () => {
		console.log(`Click on excuse: ${title}`);
	};
	return (
		<div style={styleObject(color)}>
			<img alt="demo" src={image} width="300" />
			<p>{title}</p>
			<button type="button" onClick={excuseHandler}>
				Select
			</button>
		</div>
	);
};

// (...)

Vamos a hacer que el color cambie con el hook useState(). Un hook es una funci贸n que nos permite alterar un componente.

useState(): Es un hook que importamos de REACT y empleamos para crear variables de estado (valor din谩mico) que pueden variar en el tiempo y requieran una re-renderizaci贸n del componente. Es decir, permite hacer que un componente se renderice de nuevo. Por norma se refiere a un estado visual (que condiciona el pintado visual de un componente).

El hook useState tiene la siguiente forma: siempre recibe un array donde el primer elemento del mismo es el value y el segundo elemento es una funci贸n que modifica dicho valor (setValue):

const [value, setValue] = useState();

IMPORTANTE: React solo re-pinta un componente en dos casos:

  1. Si cambia una prop
  2. Si cambia el estado de un componente (useState).

Un hook es como un enganche: este componente est谩 enganchado a un estado.

Estableciendo useState() con isActive

Nuestro nuevo hook useState inicia su valor a false. Cuando ejecutamos la funci贸n excuseHandler la variable de estado isActive modificar谩 su valor gracias a setIsActive:

Card.tsx

import React, { useState } from 'react';

const styleObject = (isActive: boolean) => ({
	border: `1px solid ${isActive ? 'green' : 'red'}`,
	padding: 10,
	marginTop: 10,
	marginLeft: '5px',
	marginRight: 5,
	display: 'inline-block',
	textAlign: 'center' as 'center',
});

const Card = (props: { ex: { title: string; image: string } }) => {
	const { ex } = props;
	const { title, image } = ex;

	const [isActive, setIsActive] = useState(false);

	const excuseHandler = () => {
		console.log(`Click on excuse: ${title}`);
		setIsActive(!isActive);
	};
	return (
		<div style={styleObject(isActive)}>
			<img alt="demo" src={image} width="300" />
			<p>{title}</p>
			<button type="button" onClick={excuseHandler}>
				Select
			</button>
			<p>status: {isActive ? 'true' : 'false'}</p>
			{isActive && <p>Excuse already used</p>}
			{!isActive && <p>excuse available</p>}
		</div>
	);
};

export default Card;

UseEffect

El hook useEfect controla cu谩ndo ejecutar el efecto colateral de algo. Posee dependencias que indican cu谩ndo ejecutar el efecto. Se usa para ejecutar c贸digo cuando se renderiza el componente o cuando cambian las dependencias del efecto. Algunos ejemplos de uso pueden ser:

  • Fetch
  • Manipulaci贸n del DOM
  • Timer functions

Vamos a instalar axios y crear una funci贸n que consuma informaci贸n de una API externa.

lib/getExcuse.ts

import axios from 'axios';

const getExcuse = async () => {
	const res = await axios.get('http://localhost:3001/excuses/random');
	const { title } = res.data;
	const wrapdata = {
		title,
	};
	console.log(wrapdata);
	return wrapdata;
};

export default getExcuse;

ExcusefromApi.tsx

import React from 'react';
import getExcuse from '../../lib/getExcuses';

const ExcusefromApi = () => {
	const ex = getExcuse();
	console.log(ex);
	return <p>ExcuseApi</p>;
};

export default ExcusefromApi;

App.tsx

import React from 'react';
import list from '../data/excuses';
import ExcusefromApi from './components/ExcusefromApi';
import Card from './components/Card';

const App = () => {
	const excusesKeys = Object.keys(list);
	return (
		<div className="cardlist">
			<ExcusefromApi />
			{excusesKeys.map((ex, i) => (
				// eslint-disable-next-line react/no-array-index-key
				<Card key={i} ex={list[ex]} />
			))}
		</div>
	);
};

export default App;

Obtenemos nueva informaci贸n gracias a ExcusefromApi. No obstante, si quisi茅rmaos trabajar la informaci贸n de la siguiente manera nos encontramos con que por consola obtenemos una promesa.

Al ser JS as铆ncrono, necesitamos esperar de alguna manera a que la promesa se resuelva antes incluso de iniciar el pintado. Podr铆amos tratar de emplear async pero obtenemos un error desde REACT:

ExcusefromApi.tsx

// (...)

const ExcusefromApi = async () => {
	const ex = await getExcuse();
	console.log(ex);
	return <p>ExcuseApi</p>;
}; // --> Uncaught Error: Objects are not valid as a React child

Estamos viendo como esta funci贸n devuelve una promesa y de que manera REACT no puede renderizar componentes que dependan de esta asincron铆a. Dicho de otra manera, cuando REACT va a pintar un componente necesita decidir que HTML va a insertar en el momento: no puede esperar a la futura resoluci贸n.Es en este punto cuando recurrimos a useEffect.

HOOK useEfect: controla cu谩ndo ejecutar el efecto colateral de algo gracias a sus dependencias.

useEffect se compone de:

  • Una funci贸n callback que contiene la l贸gica efecto-colateral y se ejecutar谩 justo despu茅s de que los cambios se ejecuten contra el DOM.
  • Un array opcional de dependencias que ejecutar谩 el callback s贸lo si estas cambian entre renderizados.
useEffect(callback[, dependencies]);

Ponemos nuestra l贸gica de efecto colateral en la funci贸n callback, y empleamos las dependencias para controlar cuando queremos que el efecto colateral se ejecute.

Sobre las dependencias del useEffect() es importante se帽alar los 3 siguientes casos:

  • No indicadas: el efecto colateral se ejecuta tr谩s cada renderizado
  • Array vac铆o []: el efecto colateral se ejecuta s贸lo una vez tras el renderizado incial
  • Tiene props o valores de estado: el efecto colateral se ejecuta s贸lo cuando el valor de alguna de las dependencias cambia.

App.tsx

import React from 'react';
import ExcusefromApi from './components/ExcusefromApi';

const App = () => (
	<div className="cardlist">
		<ExcusefromApi />
		<ExcusefromApi />
		<ExcusefromApi />
	</div>
);

export default App;

getExcuse.tsx

import axios from 'axios';

const getExcuse = async () => {
	const res = await axios.get('http://localhost:3001/excuses/random');
	const { title } = res.data;
	return title;
};

export default getExcuse;

ExcusefromApi.tsx

import React, { useState, useEffect } from 'react';
import getExcuse from '../../lib/getExcuses';
import Card from '../components/Card';

/*Cuando se monte el componente en el DOM por primera vez llama a esta funci贸n.
[] -> dependencias: indian cuando ejecutar un efecto
En este caso, al estar vac铆o el array de dependencias este efecto se ejecuta solo cuando el componente se monta por primera vez en el DOM*/

const ExcusefromApi = () => {
	const [excuse, setExcuse] = useState();

	useEffect(() => {
		getExcuse().then((ex) => {
			setExcuse(ex);
		});
	}, []);

	if (excuse) {
		return (
			<div>
				<Card excuse={excuse} />
			</div>
		);
	}
	return <p>loading excuse...</p>;
};

export default ExcusefromApi;

Card.tsx

import React, { useState } from 'react';

const styleObject = (isActive: boolean) => ({
	// (...)
});

const Card = (props: { excuse: string }) => {
	const { excuse } = props;
	const image =
		'https://preview.redd.it/0q9k35rs52461.jpg?width=1920&format=pjpg&auto=webp&s=28a96947814b6207c596c34b4e623bc6c9683692';

	const [isActive, setIsActive] = useState(false);

	const excuseHandler = () => {
		setIsActive(!isActive);
	};
	return (
		<div style={styleObject(isActive)}>
			<img alt="demo" src={image} width="300" />
			<p>{excuse}</p>
			<button type="button" onClick={excuseHandler}>
				Select
			</button>
			<p>status: {isActive ? 'true' : 'false'}</p>
			{isActive && <p>Excuse already used</p>}
			{!isActive && <p>excuse available</p>}
		</div>
	);
};

export default Card;

About

馃 Mi primer proyecto en REACT: Una completa introducci贸n a esta librer铆a

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published