React III
Controlled fields, useEffect
Contenido
- Controlled Inputs
- Concepto de Hook
- Hook
useEffect - Introducción a API para la descarga de datos
Controlled Inputs
Motivación
Hasta ahora se ha trabajado con eventos capturando acciones del usuario.
Sin embargo, al trabajar con formularios surge una cuestión fundamental:
¿Quién controla el valor del input?
¿El DOM o React?
Recordemos que en HTML puro o, el valor de un <input> está gestionado directamente por el DOM.
En React, podemos hacer que el valor del input esté controlado por el estado del componente.
A esto lo llamamos controlled input.
¿Qué es un controlled input?
Un controlled input es un elemento de formulario cuyo valor:
- Está almacenado en el estado del componente
- Se actualiza mediante un evento (
onChange) - Se renderiza utilizando ese mismo estado
Única fuente de verdad (single source of truth).
Estructura del componente controlled
import { useState } from "react";
function FormularioNombre() {
const [nombre, setNombre] = useState("");
function manejarCambio(event) {
setNombre(event.target.value);
}
return (
<div>
<input type="text"
value={nombre}
onChange={manejarCambio}
/>
<p>El nombre introducido es: {nombre}</p>
</div>
);
}Análisis del ejemplo
El valor del input se sincroniza con el estado
- Declaramos una variable de estado: nombre
- Asociamos el atributo value del input a ese estado
- Capturamos el evento onChange
- Actualizamos el estado con setNombre
Cada vez que el estado cambia: El componente se vuelve a ejecutar El JSX se vuelve a generar El valor del input se sincroniza con el estado
Ejemplo Formulario
import { useState } from "react";
function FormularioSimple() {
const [email, setEmail] = useState("");
function handleChange(event) {
setEmail(event.target.value);
}
function handleSubmit(event) {
event.preventDefault(); // Evita la recarga del navegador
console.log("Formulario enviado");
console.log("Email:", email);
setEmail(""); // Limpieza opcional del campo
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={handleChange}
/>
<button type="submit">Enviar</button>
</form>
);
}Referencia: https://es.react.dev/reference/react-dom/components/input#controlling-an-input-with-a-state-variable
Hooks (Repaso)
- Función especial que permite modificar las propiedades internas de los componentes
- Características avanzadas
- Modificación del
life-cycle - Normalmente forma
use____
useEffect
useEffectes un Hook que permite ejecutar efectos secundarios dentro de un componente funcional.- Un efecto secundario es cualquier operación que no sea simplemente devolver JSX (por ejemplo: peticiones a un servidor, temporizadores, suscripciones).
- Se ejecuta después de que el componente se renderiza en el DOM.
Métodos de uso de useEffect
1. Sin array de dependencias
useEffect(() => {
console.log("Se ejecuta en cada render");
});Comportamiento:
- Se ejecuta después de cada renderizado.
- No existe control sobre la frecuencia.
- Puede generar bucles si modifica estado sin control.
Interpretación conceptual:
El efecto acompaña siempre al render.
2. Con array vacío
useEffect(() => {
console.log("Se ejecuta solo una vez");
}, []);Comportamiento:
- Se ejecuta únicamente tras el primer render.
- Equivalente conceptual al “montaje” del componente.
Uso habitual:
- Peticiones a servidor
- Inicializaciones
- Suscripciones
Interpretación conceptual:
El efecto ocurre una única vez al crear el componente.
3. Con dependencias específicas
useEffect(() => {
console.log("Se ejecuta cuando cambia contador");
}, [contador]);Comportamiento:
- Se ejecuta tras el primer render.
- Se vuelve a ejecutar cuando cambia alguna variable del array.
Interpretación conceptual:
El efecto depende del estado indicado.
React compara el valor anterior con el nuevo y decide si debe ejecutar el efecto.
fetch()
Hasta ahora nuestros componentes eran completamente locales.
Sin embargo, en aplicaciones reales necesitamos:
- Descargar datos
- Conectarnos a servidores
- Trabajar con APIs externas
Para ello utilizamos fetch().
Ejemplo mínimo de fetch
fetch("https://pokeapi.co/api/v2/pokemon/1")
.then(response => response.json())
.then(data => console.log(data));Utiliza las Promises
Interpretación simple:
- Se hace una petición a una URL
- Se transforma la respuesta a formato JSON
- Se accede a los datos
fetch() dentro de useEffect
En React, lo habitual es realizar la descarga cuando el componente se monta.
Para ello:
- Utilizamos
useEffect - Añadimos array vacío
[] - Guardamos los datos en el estado
Ejemplo integrado
import { useState, useEffect } from "react";
function Pokemon() {
const [pokemon, setPokemon] = useState(null);
useEffect(() => {
fetch("https://pokeapi.co/api/v2/pokemon/1")
.then(response => response.json())
.then(data => setPokemon(data));
}, []);
if (!pokemon) return <p>Cargando...</p>;
return (
<div>
<h2>{pokemon.name}</h2>
<img
src={pokemon.sprites.front_default}
alt={pokemon.name}
/>
</div>
);
}Flujo conceptual
- Render inicial
- Se ejecuta
useEffect
- Se lanza la petición
- Se actualiza el estado
- Nuevo render con los datos
Advertencia importante
Si eliminamos el array vacío:
useEffect(() => {
fetch("https://pokeapi.co/api/v2/pokemon/1")
.then(res => res.json())
.then(data => setPokemon(data));
});El efecto se ejecutará en cada render.
Como actualizar estado provoca render, se generará un bucle infinito.
useEffect conecta React con el exterior.
fetch() permite traer datos del exterior.
El estado vuelve a controlar el render.
En la siguiente sección formalizaremos esto utilizando async / await.
async y await
Hasta ahora hemos utilizado fetch() con encadenamiento.
Existe una forma más clara y estructurada de escribir código asíncrono:
asyncawait
Su objetivo es hacer que el código sea más legible.
¿Qué significa async?
- Se coloca delante de una función
- Indica que la función trabajará con operaciones asíncronas
async function ejemplo() {
console.log("Función asíncrona");
}¿Qué significa await?
- Solo puede utilizarse dentro de una función
async - Detiene la ejecución hasta que la operación finaliza
- Hace que el código se lea de forma secuencial
Ejemplo básico con fetch
async function obtenerPokemon() {
const response = await fetch(
"https://pokeapi.co/api/v2/pokemon/1"
);
const data = await response.json();
console.log(data);
}Interpretación conceptual:
- Se realiza la petición
- Se espera la respuesta
- Se transforma a JSON
- Se trabaja con los datos
El código se lee de arriba hacia abajo.
async / await dentro de useEffect
En React, lo habitual es declarar una función interna y ejecutarla.
Ejemplo completo
import { useState, useEffect } from "react";
function Pokemon() {
const [pokemon, setPokemon] = useState(null);
useEffect(() => {
async function fetchData() {
const response = await fetch(
"https://pokeapi.co/api/v2/pokemon/1"
);
const data = await response.json();
setPokemon(data);
}
fetchData();
}, []);
if (!pokemon) return <p>Cargando...</p>;
return (
<div>
<h2>{pokemon.name}</h2>
<img
src={pokemon.sprites.front_default}
alt={pokemon.name}
/>
</div>
);
}¿Por qué no hacer directamente?
useEffect(async () => {
...
}, []);Porque useEffect no debe recibir una función asíncrona directamente.
Por eso declaramos la función dentro y luego la ejecutamos.
Flujo mental con async / await
- Render inicial
- Se ejecuta
useEffect
- Se llama a la función asíncrona
awaitespera la respuesta
- Se actualiza el estado
- Nuevo render
async / await no cambia lo que hace el programa.
Solo cambia la forma de escribirlo:
Más claro.
Más estructurado.
Más fácil de mantener.
