Fundamentos de React
Escribir JSX
Como ha visto, hemos estado usando lo que parece HTML en nuestro código React, pero no es HTML, sino JSX (JavaScript XML). El uso de JSX no es obligatorio para escribir React.
Debajo del "capó", se está ejecutando createElement para crear los elementos.
JSX está más cerca de JavaScript que de HTML. Hay algunas diferencias clave a tener en cuenta al escribirlo.
- className se usa en lugar de class para agregar clases CSS, ya que class es una palabra clave reservada en JavaScript.
- Las propiedades, atributos y métodos en JSX son camelCase.
- todas las etiquetas tienen que cerrarse, incluso las de cierre automático
<img src="" alt="" />
. - Un componente no puede devolver varias etiquetas JSX. Tienes que envolverlos en un contenedor,
<div> </div>
, un contenedor vacío< > < />
o<Fragment> </Fragment>
.
const App = () => {
return (
<div>
<p>Esto es un parrafo</p>
<img src="https://picsum.photos/200/300" alt="" />
</div>
)
}
export default App
Si tienes mucho HTML que convertir a JSX, puedes utilizar un convertidor en línea.
Añadir estilos
Para dar estilo a los componentes de React, utilizamos className
en lugar de class
para aplicar clases de CSS. Es recomendable escribir el CSS en archivos separados, lo que facilita la organización y mantenimiento del código en proyectos de mayor envergadura.
const Parrafo = () => {
return (
<div>
<p className="text-center">Esto es un parrafo</p>
<img src="https://picsum.photos/200/300" alt="" />
</div>
)
}
/* In your CSS */
.text-center {
text-align: center;
}
Nota: Importa el archivo CSS en tu componente con import './MiComponente.css'; para aplicar los estilos.
Interolación
Las expresiones de JavaScript también se pueden incrustar dentro de JSX usando llaves {}
, incluidas variables, funciones y propiedades.
const App = () => {
const title = "Titulo desde React"
const imagen = "src/assets/images/imagen1.jpg"
const classTitle = "text-center"
return (
<div>
<h1 className='text-center'>{title}</h1>
<h1 className='text-center'>{title.toUpperCase()}</h1>
<h1 className={classTitle}>{title.toUpperCase()}</h1>
<h2>Componente variables {1+1}</h2>
{/* <img src="src/assets/images/imagen1.jpg" alt="" /> */}
<img src={imagen} alt="" />
<img src={imagen} alt={`imagen - ${title}`} />
</div>
)
}
export default App
Componentes
En React, las aplicaciones están compuestas de componentes. Cada componente es una unidad independiente que representa una parte específica de la interfaz de usuario (IU), con su propia lógica y apariencia. Estos componentes pueden variar en tamaño y complejidad, desde algo tan sencillo como un botón hasta algo tan grande como una página completa.
const App = () => {
const title = "Titulo desde React"
return (
<div>
<h1>{title}</h1>
<MyButton />
</div>
);
};
export default App;
Los nombres de los componentes de React siempre deben comenzar por mayúscula, para diferenciar estos componentes personalizados de las etiquetas HTML estándar.
Nota: En este ejemplo hemos colocado dos componentes en el mismo archivo para comenzar a usarlos, pero en una aplicación de React es recomendable que cada componente tenga su propio archivo. Esto facilita la organización y el mantenimiento del código, especialmente en proyectos grandes. Pronto comenzaremos a estructurarlos de esta manera.
Renderizado condicional
El renderizado condicional en React es una técnica que te permite mostrar o ocultar componentes en función de ciertas condiciones, como el estado de la aplicación o alguna variable.
const MyButton = () => {
return <button>i'am a button</button>;
};
const UserMessage = () => {
return <h2>Bienvenido usuario</h2>;
};
const App = () => {
const title = "Titulo desde React"
const user = true;
return (
<div>
<h1>{title}</h1>
<MyButton />
{user ? <UserMessage /> : "Offline"}
</div>
);
};
export default App;
Cuando no necesites la rama else, puedes también usar la sintaxis lógica &&, más breve:
<div>
{user ? && <UserMessage />}
</div>
Renderizado de listas en React
El renderizado de listas es una tarea común en React. Vemos los conceptos clave y las mejores prácticas.
Uso de map()
para renderizar listas
React utiliza el método map()
de JavaScript para transformar arrays de datos en listas de elementos JSX.
Importancia del atributo key
- Propósito: React usa
key
para identificar qué elementos han cambiado, se han añadido o eliminado. - Regla: Cada elemento en una lista debe tener una
key
única entre sus hermanos.
Ejemplo de renderizados de listas
Usar el valor como key (si son únicos):
<ul>
{mesas.map(mesa => (
<li key={mesa}>{mesa}</li>
))}
</ul>
Esto es válido si cada valor en el array es único y no cambia.
Usar el índice como key (con precaución):
const mesas = ["Mesa 1", "Mesa 2", "Mesa 3"];
<ul>
{mesas.map((mesa, index) => (
<li key={index}>{mesa}</li>
))}
</ul>
Esto funciona bien para listas estáticas, pero puede causar problemas si la lista es dinámica.
Ejemplo de lista dinámica
Imagina que la lista de tareas puede cambiar con el tiempo. Aunque no mostremos cómo se actualiza, es importante usar keys únicas:
const App = () => {
const tareas = [
{ id: 1, texto: "Comprar leche" },
{ id: 2, texto: "Hacer ejercicio" },
{ id: 3, texto: "Estudiar React" }
];
return (
<div>
<h2>Mis tareas</h2>
<ul>
{tareas.map(tarea => (
<li key={tarea.id}>{tarea.texto}</li>
))}
</ul>
</div>
);
};
export default App;
¿Cuándo es importante añadir keys?
Es especialmente importante añadir keys en los siguientes casos:
- Listas dinámicas: Cuando la lista puede cambiar (añadir, eliminar o reordenar elementos).
- Elementos con estado: Si los elementos de la lista mantienen su propio estado.
- Rendimiento: En listas grandes para optimizar la actualización del DOM.
Compartir datos entre componentes - props
Las props (abreviatura de "properties") se utilizan para enviar información desde un componente padre hacia sus componentes hijos. De esta manera, los componentes pueden recibir datos y personalizar su contenido de forma dinámica.
const MyButton = (props) => {
console.log(props)
return <button>{props.text}</button>;
};
const App = () => {
const title = "Titulo desde React"
return (
<div>
<h1>{title}</h1>
<MyButton text="botón 1"/>
<MyButton text="botón 2"/>
<MyButton text="botón 3"/>
</div>
);
};
export default App;
PropTypes
- Proporciona una forma de documentar y validar las props que se esperan en un componente.
- Ayuda a evitar errores y facilita el desarrollo y mantenimiento del código.
import PropTypes from "prop-types";
const MyButton = (props) => {
return <button>{props.text}-{props.edad}</button>;
};
MyButton.propTypes = {
text: PropTypes.string.isRequired,
edad: PropTypes.number.isRequired,
};
const App = () => {
const title = "Titulo desde React"
return (
<div>
<h1>{title}</h1>
<MyButton text="botón 1" edad={18}/>
</div>
);
};
export default App;
Tipos comunes de PropTypes
PropTypes.string
: Valida que la prop sea una cadena de texto.PropTypes.number
: Valida que la prop sea un número.PropTypes.bool
: Valida que la prop sea un valor booleano (true o false).PropTypes.array
: Valida que la prop sea un array.PropTypes.object
: Valida que la prop sea un objeto.PropTypes.func
: Valida que la prop sea una función.PropTypes.symbol
: Valida que la prop sea un símbolo.PropTypes.node
: Valida que la prop pueda ser cualquier cosa que se pueda renderizar en React (elemento React, cadena de texto, número, fragmento, etc.).PropTypes.element
: Valida que la prop sea un único elemento React (es decir, no un fragmento ni una cadena de texto).PropTypes.instanceOf(Constructor)
: Valida que la prop sea una instancia de una clase específica.PropTypes.oneOf([val1, val2, ...])
: Valida que la prop sea uno de los valores proporcionados en un array.PropTypes.oneOfType([type1, type2, ...])
: Valida que la prop cumpla con al menos uno de los tipos de datos especificados en un array.PropTypes.arrayOf(type)
: Valida que la prop sea un array que contenga elementos del tipo especificado.PropTypes.objectOf(type)
: Valida que la prop sea un objeto cuyos valores sean del tipo especificado.PropTypes.shape({key: type, ...})
: Valida que la prop sea un objeto con una estructura específica.
Nota: Puedes añadir .isRequired
a cualquier tipo para indicar que la prop es obligatoria.
Ejemplo:
MyComponent.propTypes = {
optionalArray: PropTypes.array,
requiredString: PropTypes.string.isRequired,
optionalObject: PropTypes.shape({
name: PropTypes.string,
age: PropTypes.number
})
};
Eventos
En React, los eventos se manejan de forma similar a los eventos en el DOM, pero con algunas diferencias clave. Los eventos de React se nombran usando camelCase en lugar de minúsculas, lo que mantiene la convención de nomenclatura en el código.
Además, en JSX, se pasa una función como el manejador del evento, en vez de un string como en HTML. Esto permite un control más directo y flexible sobre cómo los eventos afectan a los componentes de la aplicación.
const MyButton = () => {
const handleClick = () => {
console.log("me diste click");
};
return <button onClick={handleClick}>i'am a button</button>;
};
Modularizar con componentes
Los componentes permiten separar la interfaz de usuario en piezas independientes, reutilizables y pensar en cada pieza de forma aislada.
components/MyButton.jsx
const MyButton = () => {
const handleClick = () => {
console.log("me diste click");
};
return <button onClick={handleClick}>i'am a button</button>;
};
export default MyButton;
App.jsx
import MyButton from "./components/MyButton";
const App = () => {
return (
<div>
<h1>{title}</h1>
<MyButton />
</div>
);
};
export default App;
Estados en React
El estado permite a los componentes de React cambiar su salida a lo largo del tiempo en respuesta a acciones del usuario, respuestas de red y cualquier otra cosa.
Problema con variables locales
Veamos primero por qué necesitamos el estado:
export default () => {
let counter = 0;
const handleClickIncrement = () => {
counter++;
console.log(counter);
};
return <button onClick={handleClickIncrement}>Counter: {counter}</button>;
};
Este código no funcionará como esperamos porque:
- Las variables locales no persisten entre renderizados.
- Los cambios en variables locales no provocan una re-renderización.
Uso de useState
Para solucionar esto, usamos el Hook useState
:
import { useState } from "react";
export default () => {
const [counter, setCounter] = useState(0);
const handleClickIncrement = () => {
setCounter(counter + 1);
// setCounter((prevCounter) => prevCounter + 1);
};
return <button onClick={handleClickIncrement}>Counter: {counter}</button>;
};
Puntos clave sobre useState:
useState
devuelve un array con dos elementos: el valor actual del estado y una función para actualizarlo.- Podemos usar la desestructuración de arrays para asignar nombres a estos elementos.
- El argumento pasado a
useState
es el valor inicial del estado. - Cuando actualizamos el estado con la función setter, React re-renderiza el componente.
- Podemos usar el valor anterior del estado pasando una función a la función setter.
Cuándo usar useState
- Para datos que cambian con el tiempo.
- Para información que el componente necesita "recordar" entre renderizados.
- Cuando quieres que un cambio en los datos provoque una re-renderización del componente.
Para saber más sobre estados
Para saber más sobre responder a eventos