El reto: une todo el curso
Hasta aquí aprendiste: componentes y props, JSX, estado con useState y efectos con useEffect.
Ahora vamos a unirlo todo en una app real: una lista de tareas (una todo app). Es el
proyecto clásico para dominar lo fundamental de React, porque toca destrezas en combinación:
- Añadir tareas al escribir y pulsar un botón (estado).
- Dibujar la lista con
.map(JSX + listas). - Guardar y recuperar las tareas con
useEffect.
Nos apoyamos en el hook useState para la lista y los campos, y en useEffect para
guardar automáticamente las tareas en el navegador.
Paso 1: el estado de la app
Definimos el estado que necesita la app: la lista de tareas y el texto del campo de entrada.
import { useState, useEffect } from "react";
function App() {
const [tareas, setTareas] = useState([]);
const [texto, setTexto] = useState("");
return (
<div>
<h1>Mis tareas</h1>
{/* Aquí va el formulario y la lista */}
</div>
);
}
Fíjate cómo dividimos el problema: tareas guarda la lista, texto
guarda lo que el usuario está escribiendo ahora mismo en el campo. Son dos estados porque son
dos datos independientes.
Paso 2: añadir una tarea
Definimos la función que recibe el texto actual y lo agrega a la lista. Recuerda: nunca mutamos el array directamente; creamos uno nuevo.
function agregarTarea() {
if (texto.trim() === "") return; // Ignoramos vacíos
setTareas([...tareas, { id: Date.now(), titulo: texto }]);
setTexto(""); // Limpiamos el campo
}
El operador spread [...tareas, nueva] crea un array nuevo que copia las
tareas existentes y añade la nueva al final. En React es la forma correcta de "actualizar" un
array guardado en estado: no modificamos el original, creamos uno nuevo.
Paso 3: el render con el formulario y la lista
Enlazamos el campo controlado (onChange), el botón (onClick) y dibujamos la lista con .map.
return (
<div>
<h1>Mis tareas</h1>
<input
type="text"
value={texto}
onChange={(e) => setTexto(e.target.value)}
placeholder="Escribe una tarea"
/>
<button onClick={agregarTarea}>Añadir</button>
<ul>
{tareas.map((t) => (
<li key={t.id}>{t.titulo}</li>
))}
</ul>
</div>
);
Cada tarea necesitó un id único. Usamos Date.now() (los milisegundos
actuales), que casi siempre es único, para el key del .map.
Paso 4: persistir con useEffect
Queremos que las tareas no se pierdan al recargar la página. La guardamos en
localStorage del navegador. Cada vez que tareas cambia, el efecto la guarda:
useEffect(() => {
localStorage.setItem("tareas", JSON.stringify(tareas));
}, [tareas]);
Y al montar (solo una vez, array []), recuperamos lo guardado:
useEffect(() => {
const guardadas = localStorage.getItem("tareas");
if (guardadas) {
setTareas(JSON.parse(guardadas));
}
}, []);
JSON.stringify convierte el array en texto para guardarlo en
localStorage, y JSON.parse hace lo inverso: convierte el texto de
vuelta en array. Es la forma estándar de persistir datos simples en el navegador.
La app completa
import { useState, useEffect } from "react";
function App() {
const [tareas, setTareas] = useState([]);
const [texto, setTexto] = useState("");
// Cargar tareas guardadas al montar
useEffect(() => {
const guardadas = localStorage.getItem("tareas");
if (guardadas) setTareas(JSON.parse(guardadas));
}, []);
// Guardar cada vez que cambien las tareas
useEffect(() => {
localStorage.setItem("tareas", JSON.stringify(tareas));
}, [tareas]);
function agregarTarea() {
if (texto.trim() === "") return;
setTareas([...tareas, { id: Date.now(), titulo: texto }]);
setTexto("");
}
return (
<div>
<h1>Mis tareas</h1>
<input
type="text"
value={texto}
onChange={(e) => setTexto(e.target.value)}
placeholder="Escribe una tarea"
/>
<button onClick={agregarTarea}>Añadir</button>
<ul>
{tareas.map((t) => (
<li key={t.id}>{t.titulo}</li>
))}
</ul>
</div>
);
}
Para recordar
- Un proyecto real combina props, JSX, estado y efectos al mismo tiempo.
- Nunca mutes un array en estado: usa spread
[...array, nuevo]para crear uno nuevo. - Usa
localStorage+useEffectpara que los datos sobrevivan a la recarga. - Todo componente devuelve un solo elemento raíz (usa fragmentos si hace falta).
🎉 ¡Felicidades! Terminaste el curso de React. Ya sabes dividir una interfaz en componentes,
escribir JSX, pasar datos con props, manejar estado con useState y efectos con useEffect, y
construir una app real. En la próxima entrega profundizaremos en hooks avanzados, formularios
complejos y cómo conectar tu app con APIs y enrutado con frameworks como Next.js.