Framework: ReactJS#

Logo Angular

Documentación básica de ReactJS

Configuraciones#

Instalar React#

Paso 1: Tener instalado NodeJS desde su página oficial: https://nodejs.org/es/ Paso 2: Instalar React con npx desde la carpeta del proyecto: npx create-react-app nombre-proyecto Paso 3: Acceder a la carpeta del proyecto y ejecutarlo: npm start Paso 4: Se abre el navegador con la ruta: http://localhost:3000

Atención

npx viene instalado con npm y es su versión en la nube. De modo que si necesitas un paquete y no deseas instalarlo en tu máquina utiliza npx en lugar de npm.

Herramientas de desarrollo#

Existen una serie de herramientas útiles que se pueden instalar desde la tienda de tu navegador:

  • React Developer Tools

También existe una herramienta muy útil para Visual Studio Code:

  • ES7 React/Redux/GraphQL/React-Native snippets

Importar React desde CDN#

Existe la opción de importar react desde un cdn en lugar de crear un proyecto con node:

 1<!DOCTYPE html>
 2<html lang="en">
 3<head>
 4    <meta charset="UTF-8">
 5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 6    <title>React prueba</title>
 7
 8</head>
 9<body>
10    <div id="raiz"></div>
11
12    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
13    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
14    <!-- React utiliza babel para poder trabajar con JSX -->
15    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.18.13/babel.min.js"></script>
16    <!-- Definimos que el tipo de script será de babel: -->
17    <script type="text/babel">
18        const raiz = document.querySelector('#raiz');
19        const nombre = "Guillermo";
20        // Las llaves nos permiten incrustar código JSX:
21        const titulo = <h1>Te llamas {nombre}</h1>;
22
23        ReactDOM.render(titulo, raiz);
24    </script>
25</body>
26</html>

Estructura proyecto React#

  • Carpeta Public: Aquí irá todo los archivos públicos como imágenes, iconos y algún que otro html.

  • Carpeta src: aquí viene el contenido del proyecto.
    • index.js: Archivo de inicio principal de la aplicación. Tiene su homónimo css disponible para los estilos globales.

    • App.js: Componente principal de la aplicación donde se irán cargando el resto de componentes.

Dentro de src podemos crear la siguiente estructura de subcarpetas basado en Atomic Design:

  • common: para componentes que se van a reutilizar en distintos sitios de la aplicación.

  • templates: para componentes que pintan diferentes páginas en la aplicación.

  • Router.js: para los componentes que se relacionan con el enrutamiento (React Router).

  • pages: para los componentes que pintan vistas, dentro de esta carpeta se crean otras subcarpetas para componentes como Index, Login, Shop.. y dentro cada uno de sus componentes de un solo uso.

  • requests: para los archivos que consumen servicios rest.

Preparar App.js#

Se borra el contenido de App.js y App.css dentro de la carpeta src y se edita App.js:

 1// Se importa el css si existe:
 2import './App.css';
 3
 4// se crea una función:
 5function App() {
 6// esta función retorna un nodo con todas las etiquetas html:
 7return (
 8    <h1>Soy un componente de prueba</h1>
 9);
10}
11
12// se exporta el componente como un módulo:
13export default App;

Nota

index.js se queda vinculado como nodo principal hacia el archivo index.html el resto irán ligados a app.js

Componentes#

Los componentes basados en funciones son los más modernos y recomendados para uso de hooks.

Crear un componente#

Crear un componente: en src crear un archivo llamado Prueba.js:

 1// se crea una función con el componente:
 2function Prueba(){
 3    // el retorno del componente será el contenido html:
 4    return(
 5        <div>
 6            <h1>Componente de prueba</h1>
 7        </div>
 8    );
 9}
10
11export default Prueba;

Atención

Por convención el nombre del componente comienza en Mayúscula y el contenido html de return irá siempre envuelto en etiquetas <div>

Crear css del componente#

Además del css principal de App.css cada componente lleva su propio archivo css con el mismo nombre Prueba.css:

1h1{
2    color: blue;
3}

Utilizar componente#

Para utilizar el componente, es necesario cargarlo en otro componente que este funcionando, actualmente App.js:

 1import './App.css';
 2// importar componente:
 3import Prueba from './Prueba';
 4
 5function App() {
 6return (
 7    <div>
 8    <h1>Recuerda usar contenedores div sino dará errores</h1>
 9    {/* cargar componente (metodo para hacer comentarios en el return): */}
10    <Prueba />
11    </div>
12);
13}
14
15export default App;

Uso de fragment#

Fragment te permite cargar varios nodos sin tener que añadir al DOM etiquetas div:

 1// importar fragment:
 2import {Fragment} from 'react';
 3import './App.css';
 4import Prueba from './Prueba';
 5
 6function App() {
 7return (
 8    <Fragment>
 9    <h1>El tag fragment omite el uso de divs</h1>
10    <Prueba />
11    </Fragment>
12);
13}
14
15export default App;

Props: Comunicación de padre a hijo#

Los props permiten enviar datos desde componentes padre a hijos

Enviando propiedades#

  • Editar el componente padre (en este ejemplo App.js):

 1import {Fragment} from 'react';
 2import './App.css';
 3import Prueba from './Prueba';
 4
 5function App() {
 6return (
 7    <Fragment>
 8    <h1>Listado de consolas: </h1>
 9    <Prueba marca="Nintendo" modelo="Wii" />
10    <Prueba marca="Nintendo" modelo="Switch" />
11    <Prueba marca="Nintendo" modelo="Gamecube" />
12    </Fragment>
13);
14}
15
16export default App;

Recuperando propiedades#

Recuperar propiedades en el componente hijo (en este caso Prueba.js):

 1import './Prueba.css';
 2
 3import './Prueba.css';
 4
 5// cargar propiedades (o bien escribimos props y sacamos props.marca, props.modelo o desestructuramos como en este caso):
 6function Prueba({marca, modelo}){
 7
 8    return(
 9        <div>
10            {/* Cargar la información: */}
11            <h1>- {marca} {modelo}</h1>
12        </div>
13    );
14}
15
16export default Prueba;

Propiedades por defecto (default props)#

Cuando no se reciben propiedades se pueden establecer algunas por defecto en el componente que completan uno o varios campos no recibidos:

 1import './Prueba.css';
 2
 3function Prueba({marca, modelo}){
 4    return(
 5        <div>
 6            <p> {marca} {modelo}</p>
 7        </div>
 8    );
 9}
10
11// si el componente no recibe propiedades añade estas:
12Prueba.defaultProps = {marca: "Genérica", modelo: "estandar"}
13
14export default Prueba;

Validar propiedades (PropTypes)#

Se pueden validar los campos recibidos, de manera que cuando uno no cumpla con el formato establecido nos avise por consola:

 1// importar PropTypes:
 2import PropTypes from 'prop-types';
 3import './Prueba.css';
 4
 5function Prueba({marca, modelo, lanzamiento}){
 6    return(
 7        <div>
 8            <p> {marca} {modelo} de {lanzamiento}</p>
 9        </div>
10    );
11}
12
13// crear validador:
14Prueba.propTypes = {
15    marca: PropTypes.string.isRequired,
16    modelo: PropTypes.string,
17    lanzamiento: PropTypes.number
18}
19
20export default Prueba;

Comunicación de hijo a padre#

  1. Desde el componente hijo tenemos lo siguiente:

 1// importar el hook:
 2import {useState} from 'react';
 3import './Prueba.css';
 4
 5// la función recibe un hook con los valores:
 6function Prueba({setJuegos}){
 7    // crear un nuevo hook para cambiar el estado del actual:
 8    const [nuevoJuego, setNuevoJuego] = useState('');
 9
10    // el hook tendra un handle para cambiar su estado:
11    const handleNuevoJuego = (e) => {
12        setNuevoJuego(e.target.value);
13    }
14
15    // crear un handle que añadira el juego nuevo:
16    const handleJuegos = (e) => {
17        // recuerda prevenir refresco de pantalla:
18        e.preventDefault();
19        // utilizar el hook del padre para añadir el valor del hook del hijo a una lista:
20        setJuegos(juego => [...juego, nuevoJuego]);
21        // regresar a su estado vacio el hook del hijo:
22        setNuevoJuego('');
23    }
24
25    // retornar el input donde introducir nuevos valores:
26    return(
27        <form onSubmit={handleJuegos}>
28            <input type="text" value={nuevoJuego} onChange={handleNuevoJuego} />
29        </form>
30    )
31}
32
33export default Prueba;
  1. Y desde el padre:

 1import {Fragment} from 'react';
 2// importar el hook:
 3import {useState} from 'react';
 4// importar el hijo:
 5import Prueba from './Prueba.js';
 6import './App.css';
 7
 8function App() {
 9// tenemos el hook principal con los juegos:
10const [juegos, setJuegos] = useState(['Zelda', 'Mario', 'Yoshi']);
11
12// en el return añadimos el componente al formulario y le pasamos el hook de juegos haciendo el cambio de estado:
13return(
14    <Fragment>
15    <Prueba setJuegos={setJuegos} />
16    <ul>
17        {
18        juegos.map((juego, index) => {
19            return <li key={index}>{juego}</li>
20        })
21        }
22    </ul>
23    </Fragment>
24)
25
26}
27
28export default App;

Nota

Se puede añadir un valor index en los mapeos para evitar errores de duplicate key

JSX#

JSX es una combinación de la sintaxis de javascript con XML, similar a HTML.

Imprimir datos en JSX#

 1import './Prueba.css';
 2
 3// cargar propiedades (o bien escribimos props y sacamos props.marca, props.modelo o desestructuramos como en este caso):
 4function Prueba(){
 5    // crear una variable:
 6    const consola = {
 7        marca: "Nintendo",
 8        modelo: "DS"
 9    }
10
11    return(
12        <div>
13            {/* Cargar la información: */}
14            <h1>- {consola.marca} {consola.modelo}</h1>
15        </div>
16    );
17}
18
19export default Prueba;

Mostrar contenido de un objeto#

 1import './Prueba.css';
 2
 3function Prueba(){
 4    const consola = {
 5        marca: "Nintendo",
 6        modelo: "DS",
 7        lanzamiento: 2001
 8    }
 9
10    return(
11        <div>
12            <h1>Ver todos los valores de un objeto:</h1>
13            {/* ver valores del objeto (objeto, campos, cantidad de espacios): */}
14            <pre>{JSON.stringify(consola, null, 3)}</pre>
15            <hr/>
16            <h1>Ver solo campos determinados:</h1>
17            <pre>{JSON.stringify(consola, ['marca', 'modelo'], 5)}</pre>
18        </div>
19    );
20}
21
22export default Prueba;

Condicionales en JSX#

Si es necesario hacer una validación dentro del return se hace del siguiente modo:

Condicional simple#

 1import './Prueba.css';
 2
 3function Prueba(){
 4    const consola = {
 5        marca: "Nintendo",
 6        modelo: "DS",
 7        lanzamiento: 2001
 8    }
 9
10    return(
11        <div>
12            {consola.marca === "Nintendo" &&
13                <p>La consola es de Nintendo</p>
14            }
15        </div>
16    );
17}
18
19export default Prueba;

Condicional ternaria#

 1import './Prueba.css';
 2
 3function Prueba(){
 4    const consola = {
 5        marca: "Nintendo",
 6        modelo: "DS",
 7        lanzamiento: 2001
 8    }
 9
10    return(
11        <div>
12            {consola.marca === "Nintendo" ? (
13                <p>La consola es de Nintendo</p>
14            ) : (
15                <p>La consola es otra marca</p>
16            )}
17        </div>
18    );
19}
20
21export default Prueba;

Estilos y clases#

Añadir estilos manualmente con style#

 1import './Prueba.css';
 2
 3function Prueba(){
 4    // Utilizar la notación CamelCase en lugar de kebab-case para estilos:
 5    const estilo = {
 6        color: "red",
 7        backgroundColor: "black"
 8    }
 9
10    return(
11        <div>
12            {/* cargar datos de estilo: */}
13            <p style={estilo}>Nintendo Switch</p>
14        </div>
15    );
16}
17
18export default Prueba;

uso de clases con className#

En JSX se reemplaza el atributo class por className:

 1import './Prueba.css';
 2
 3function Prueba(){
 4    const lanzamiento = 2017;
 5
 6    return(
 7        <div>
 8            {/* implementar clase: */}
 9            <p className={"switch"}>Nintendo Switch</p>
10            {/* uso ternario de clases condicionales: */}
11            <p className={lanzamiento ? 'showLanzamiento' : 'hideLanzamiento'}>Lanzamiento: {lanzamiento}</p>
12        </div>
13    );
14}
15
16export default Prueba;

Recorrer array con map#

 1import './Prueba.css';
 2
 3function Prueba(){
 4    const consolas = ["Nintendo Switch", "Gameboy", "Master System", "Playstation"];
 5
 6    return(
 7        <div>
 8            {/* recorrer elementos con map (importante añadir una key): */}
 9            <ol>
10                {
11                    consolas.map( consola =>{
12                        return <li key={consola}>{consola}</li>
13                    })
14                }
15            </ol>
16        </div>
17    );
18}
19
20export default Prueba;

Recorrer array de objetos con map#

 1import './Prueba.css';
 2
 3function Prueba(){
 4    const consolas = [
 5        {marca: "Nintendo", modelo: "Switch"},
 6        {marca: "Sega", modelo: "Master System"},
 7        {marca: "Sony", modelo: "PlayStation"}
 8    ];
 9
10    return(
11        consolas.map( consola =>{
12            return(
13                <div key={consola.modelo}>
14                    <ul>
15                        <li>{consola.marca}</li>
16                        <li>{consola.modelo}</li>
17                    </ul>
18                </div>
19            )
20        })
21    );
22}
23
24export default Prueba;

Nota

Hemos añadido un segundo return para poder añadir más de una línea de JSX dentro del bucle.

Nota

Se puede desestructurar el elemento consola en ({marca, modelo}).

Eventos y Hooks#

Tipos de Eventos#

PORTAPAPELES#

  • onCopy

  • onCut

  • onPaste

TECLADO#

  • onKeyDown

  • onKeyUp

  • onKeyPress

RATÓN#

  • onClick

  • onContextMenu

  • onDoubleClick

  • onDrag

  • onDragEnd

  • onDragEnter

  • onDragExit

  • onDragLeave

  • onDragOver

  • onDragStart

  • onDrop

  • onMouseDown

  • onMouseEnter

  • onMouseLeave

  • onMouseMove

  • onMouseOut

  • onMouseOver

  • onMouseUp

FORMULARIOS#

  • onChange

  • onInput

  • onInvalid

  • onReset

  • onSubmit

mas eventos en: https://es.reactjs.org/docs/events.html

Uso de eventos#

Eventos sin retorno#

 1import {Fragment} from 'react';
 2import './App.css';
 3
 4function App() {
 5// función que dispara el evento:
 6const mensaje = (e) => {
 7    // lanzar mensaje con alguna propiedad del botón:
 8    alert(`se ha pulsado ${e.target.innerText}`);
 9}
10
11return (
12    <Fragment>
13    {/* botón con el evento click: */}
14    <button onClick={mensaje}>Disparar mensaje de alerta</button>
15    </Fragment>
16);
17}
18
19export default App;

Eventos con retorno y parámetros#

 1import {Fragment} from 'react';
 2import './App.css';
 3
 4function App() {
 5// se recibén en el callback los parámetros:
 6const consola = (marca, modelo) => {
 7    // los eventos que retornan algo también reciben algo:
 8    return (e) => {
 9    // ahora hay dos tipos de parámetros, los que se recibén de la función y el evento que se recibe en este caso en el return:
10
11    // parametros recibidos:
12    alert(`${marca} ${modelo}`);
13    // evento recibido por return:
14    alert(`se ha pulsado ${e.target.innerText}`);
15
16    }
17}
18
19return (
20    <Fragment>
21    {/* el evento recibe la función con los parámetros:: */}
22    <button onClick={consola('Nintendo','Switch')}>Averiguar videoconsola</button>
23    </Fragment>
24);
25}
26
27export default App;

Hooks#

Los hooks se utilizan en React para cambiar el estado de un componente, los más comunes son:

useState#

Devuelve un valor con estado y una función para actualizarlo:

 1import {Fragment} from 'react';
 2// importar el hook:
 3import {useState} from 'react';
 4import './App.css';
 5
 6function App() {
 7// el elemento consola será un hook con un valor por defecto:
 8const [consola, setConsola] = useState("ej. Gameboy");
 9
10// a continuación se usará una función que ejecute el cambio de estado:
11const consolaChange = (e) => {
12    // recuperar el valor del input:
13    setConsola(e.target.value);
14}
15
16return (
17    <Fragment>
18    <h1>¿Cuál es tu videoconsola favorita?</h1>
19    <input type="text" onChange={consolaChange} />
20    <p>Mi videoconsola favorita es: {consola}</p>
21    </Fragment>
22);
23}
24
25export default App;

useEffect#

Realiza la ejecución de código después de renderizar la pantalla. Muy útil para subscribirse a servicios rest:

 1// se importa useEffect:
 2import {useState, useEffect} from 'react';
 3
 4function App() {
 5    // se declara el hook tipo useState:
 6    const [mensaje, setMensaje] = useState(0);
 7
 8    // el código de useEffect se ejecuta una vez renderizado el componente:
 9    useEffect(
10        () => {
11            // modificación de estado:
12            window.setTimeout(()=>{
13                setMensaje(mensaje + 1);
14            }, 1000);
15        }, [mensaje] // este valor se dispara cuando detecta un cambio de estado
16    )
17
18    return <p>{mensaje}</p>;
19}
20
21export default App;

useContext#

Crea un contexto por el que se pueden enviar propiedades a cualquier componente sin tener que enviarlo por parámetros:

  1. Archivo que recibe el contexto message.js:

1// importar en el archivo React y el hook:
2import React, {useContext} from 'react';
3// crear un mensaje:
4const mensaje = "Mensaje genérico";
5// cargar en el contexto de React:
6const MensajeContext = React.createContext(mensaje);
  1. Uso del contexto:

1function App() {
2// utilizar el context sin necesidad de hacer nada mas:
3const mensaje = useContext(MensajeContext);
4return <p>{mensaje}</p>;
5}
6
7export default App;

Formularios en React#

Combinando el uso de eventos y hooks se preparan los formularios

 1import {Fragment} from 'react';
 2// importar el hook:
 3import {useState} from 'react';
 4import './App.css';
 5
 6function App() {
 7// crear los hooks para el usuario y contraseña:
 8const [usuario, setUsuario] = useState('');
 9const [password, setPassword] = useState('');
10
11// cuando escribimos en el campo se irá cambiando su estado:
12const handleUsuario = (evento) =>{
13    // se recupera el evento:
14    setUsuario(evento.target.value);
15    console.log(evento.target.value);
16}
17
18const handlePassword = (evento) =>{
19    // se recupera el evento:
20    setPassword(evento.target.value);
21}
22
23// Ejecutamos esta acción al hacer login:
24const login = (e) =>{
25    // para prevenir que refresque por defecto la página:
26    e.preventDefault();
27    if(usuario === 'guillermo' && password === "1234"){
28    alert("Sesión iniciada correctamente");
29    }else{
30    alert("Error al iniciar sesión");
31    }
32}
33
34return (
35    <>
36    <form onSubmit={login}>
37        <input type="text" placeholder="Usuario" onChange={handleUsuario} />
38        <input type="password" placeholder="Contraseña" onChange={handlePassword} />
39        <input type="submit" value="Iniciar sesión" />
40    </form>
41    </>
42);
43}
44
45export default App;

Rutas con React Router#

Para las rutas se utiliza un modulo llamado React Router

Instalación#

Instalar el módulo en el proyecto: npm install react-router-dom --save

Crear archivo de rutas#

  1. Dentro de la carpeta src crear archivo Router.js:

 1// importar funciones del modulo react router:
 2import {BrowserRouter, Route, Routes} from 'react-router-dom';
 3// importar fragment también:
 4import {Fragment} from 'react';
 5
 6// importar los componentes de vista:
 7import Inicio from './Inicio';
 8import Prueba from './Prueba';
 9import Error from './Error';
10import Parametros from './Parametros';
11
12function Router(){
13    // retornar la estructura de rutas:
14    return(
15        <Fragment>
16            <BrowserRouter>
17                <Routes>
18                    {/* Ruta raiz (necesita el atributo exact): */}
19                    <Route exact path="/" element={<Inicio />} />
20                    <Route path="/prueba" element={<Prueba />} />
21                    {/* Ruta con parametros: */}
22                    <Route path="/parametros/:nombre" element={<Parametros />} />
23                    {/* Ruta para urls no establecidas (error 404): */}
24                    <Route path="*" element={<Error />} />
25                </Routes>
26            </BrowserRouter>
27        </Fragment>
28    )
29}
30
31export default Router;
  1. Cargar el enrutador en App.js:

 1import {Fragment} from 'react';
 2import './App.css';
 3// importar router:
 4import Router from './Router';
 5
 6function App() {
 7// cargar directamente el router en el return:
 8return(
 9    <Fragment>
10    <Router />
11    </Fragment>
12)
13
14}
15
16export default App;

Recibir parámetros de una ruta#

Recibir parametros en el controlador Parametros.js:

 1// importar useParams de React Router:
 2import { useParams } from 'react-router-dom';
 3
 4function Parametros(){
 5    // cargar un parametro mediante desestructuración:
 6    const {nombre} = useParams();
 7
 8    return(
 9        <div>
10            <h1>Te llamas: {nombre}</h1>
11        </div>
12    )
13}
14
15export default Parametros;