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:
+-
+
async
+await
+
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.
+ 