MÓDULO 1: UI MODERNA

Estado (State)

El estado es la memoria de tu componente. Es lo que cambia cuando el usuario interactúa con la app. Cuando cambias el estado, React actualiza automáticamente la pantalla. Sin estado, tu app sería estática y aburrida.


1. ¿Qué es el Estado?

El estado es información que un componente "recuerda". Cuando el estado cambia, el componente se re-renderiza (se dibuja de nuevo).

import { useState } from 'react';

export default function App() {
    // useState devuelve un array con 2 elementos:
    // 1. El valor actual del estado
    // 2. Una función para cambiar el estado
    const [contador, setContador] = useState(0);

    return (
        <div>
            <h1>Contador: {contador}</h1>
            <button onClick={() => setContador(contador + 1)}>
                Aumentar
            </button>
        </div>
    );
}

Cómo funciona useState:

  • useState(0): Inicializa el estado con valor 0.
  • contador: El valor actual del estado.
  • setContador: La función para cambiar el estado.
  • Cuando llamas setContador(5), React actualiza el estado y re-renderiza el componente.

2. Estado con Diferentes Tipos de Datos

El estado puede ser cualquier tipo: números, strings, booleanos, arrays, objetos.

import { useState } from 'react';

export default function App() {
    // Estado string
    const [nombre, setNombre] = useState("");

    // Estado booleano
    const [estaVisible, setEstaVisible] = useState(true);

    // Estado array
    const [tareas, setTareas] = useState(["Tarea 1", "Tarea 2"]);

    // Estado objeto
    const [usuario, setUsuario] = useState({
        nombre: "Juan",
        edad: 25,
        ciudad: "Madrid"
    });

    return (
        <div>
            <p>Nombre: {nombre}</p>
            <p>Visible: {estaVisible ? "Sí" : "No"}</p>
            <p>Tareas: {tareas.length}</p>
            <p>{usuario.nombre} tiene {usuario.edad} años</p>
        </div>
    );
}

3. Actualizar Estado Primitivo

Para tipos simples (números, strings, booleanos), es directo.

import { useState } from 'react';

export default function App() {
    const [contador, setContador] = useState(0);
    const [nombre, setNombre] = useState("");
    const [estaEncendido, setEstaEncendido] = useState(false);

    return (
        <div>
            <!-- Actualizar número -->
            <button onClick={() => setContador(contador + 1)}>
                Aumentar
            </button>

            <!-- Actualizar string -->
            <input
                value={nombre}
                onChange={(e) => setNombre(e.target.value)}
                placeholder="Escribe tu nombre"
            />

            <!-- Actualizar booleano -->
            <button onClick={() => setEstaEncendido(!estaEncendido)}>
                {estaEncendido ? "Apagar" : "Encender"}
            </button>
        </div>
    );
}

4. Actualizar Estado de Arrays

Para arrays, debes crear uno nuevo (no modificar el original).

import { useState } from 'react';

export default function App() {
    const [tareas, setTareas] = useState(["Tarea 1", "Tarea 2"]);

    // Agregar elemento
    const agregarTarea = () => {
        setTareas([...tareas, "Nueva tarea"]);
        // [...tareas] copia el array y le agrega el nuevo elemento
    };

    // Eliminar elemento
    const eliminarTarea = (index) => {
        setTareas(tareas.filter((_, i) => i !== index));
        // filter crea un nuevo array sin el elemento en esa posición
    };

    // Modificar elemento
    const modificarTarea = (index, nuevoTexto) => {
        const nuevoArray = [...tareas];
        nuevoArray[index] = nuevoTexto;
        setTareas(nuevoArray);
    };

    return (
        <div>
            <button onClick={agregarTarea}>Agregar</button>
            <ul>
                {tareas.map((tarea, index) => (
                    <li key={index}>
                        {tarea}
                        <button onClick={() => eliminarTarea(index)}>
                            Eliminar
                        </button>
                    </li>
                ))}
            </ul>
        </div>
    );
}

5. Actualizar Estado de Objetos

Para objetos, también debes crear uno nuevo con los cambios.

import { useState } from 'react';

export default function App() {
    const [usuario, setUsuario] = useState({
        nombre: "Juan",
        edad: 25,
        ciudad: "Madrid"
    });

    // Actualizar una propiedad
    const actualizarNombre = (nuevoNombre) => {
        setUsuario({
            ...usuario, // Copiar todas las propiedades
            nombre: nuevoNombre // Sobrescribir la que cambió
        });
    };

    // Actualizar múltiples propiedades
    const actualizarUsuario = () => {
        setUsuario({
            ...usuario,
            edad: 26,
            ciudad: "Barcelona"
        });
    };

    return (
        <div>
            <p>{usuario.nombre}, {usuario.edad} años, {usuario.ciudad}</p>
            <button onClick={() => actualizarNombre("Ana")}>
                Cambiar nombre
            </button>
            <button onClick={actualizarUsuario}>
                Actualizar
            </button>
        </div>
    );
}

6. Estado Compartido Entre Componentes

A veces necesitas compartir estado entre componentes. La solución es "elevar" el estado al padre.

// Componente hijo
function Boton({ contador, setContador }) {
    return (
        <button onClick={() => setContador(contador + 1)}>
            Aumentar
        </button>
    );
}

// Componente hijo
function Pantalla({ contador }) {
    return <h1>{contador}</h1>;
}

// Componente padre (donde vive el estado)
export default function App() {
    const [contador, setContador] = useState(0);

    return (
        <div>
            <Pantalla contador={contador} />
            <Boton contador={contador} setContador={setContador} />
        </div>
    );
}

7. Caso Práctico: Carrito de Compras

Crea un carrito que agregue y elimine productos.

import { useState } from 'react';

export default function Carrito() {
    const [carrito, setCarrito] = useState([]);
    const [producto, setProducto] = useState("");
    const [precio, setPrecio] = useState("");

    const agregarProducto = () => {
        if (producto && precio) {
            setCarrito([...carrito, {
                id: Date.now(),
                nombre: producto,
                precio: parseFloat(precio)
            }]);
            setProducto("");
            setPrecio("");
        }
    };

    const eliminarProducto = (id) => {
        setCarrito(carrito.filter((item) => item.id !== id));
    };

    const total = carrito.reduce((sum, item) => sum + item.precio, 0);

    return (
        <div>
            <h1>Carrito de Compras</h1>

            <div>
                <input
                    value={producto}
                    onChange={(e) => setProducto(e.target.value)}
                    placeholder="Producto"
                />
                <input
                    type="number"
                    value={precio}
                    onChange={(e) => setPrecio(e.target.value)}
                    placeholder="Precio"
                />
                <button onClick={agregarProducto}>
                    Agregar
                </button>
            </div>

            <ul>
                {carrito.map((item) => (
                    <li key={item.id}>
                        {item.nombre} - ${item.precio}
                        <button onClick={() => eliminarProducto(item.id)}>
                            Eliminar
                        </button>
                    </li>
                ))}
            </ul>

            <h2>Total: ${total.toFixed(2)}</h2>
        </div>
    );
}

8. Caso Práctico: Formulario con Estado

Crea un formulario que valide y guarde datos.

import { useState } from 'react';

export default function Formulario() {
    const [datos, setDatos] = useState({
        nombre: "",
        email: "",
        contraseña: ""
    });

    const [enviado, setEnviado] = useState(false);

    const handleChange = (e) => {
        const { name, value } = e.target;
        setDatos({
            ...datos,
            [name]: value
        });
    };

    const handleSubmit = (e) => {
        e.preventDefault();
        console.log("Datos enviados:", datos);
        setEnviado(true);
        setDatos({ nombre: "", email: "", contraseña: "" });
    };

    return (
        <div>
            {enviado && <p>✓ Formulario enviado correctamente</p>}

            <form onSubmit={handleSubmit}>
                <input
                    type="text"
                    name="nombre"
                    value={datos.nombre}
                    onChange={handleChange}
                    placeholder="Nombre"
                    required
                />

                <input
                    type="email"
                    name="email"
                    value={datos.email}
                    onChange={handleChange}
                    placeholder="Email"
                    required
                />

                <input
                    type="password"
                    name="contraseña"
                    value={datos.contraseña}
                    onChange={handleChange}
                    placeholder="Contraseña"
                    required
                />

                <button type="submit">
                    Enviar
                </button>
            </form>
        </div>
    );
}

🛠️ Tu Misión

Usa CodeSandbox (React).

  1. Crea un componente con un contador que aumente y disminuya.
  2. Crea un componente que cambie entre dos estados (encendido/apagado).
  3. Crea un input que actualice el estado mientras escribes.
  4. Crea una lista de tareas donde puedas agregar y eliminar tareas.
  5. Crea un formulario que guarde los datos en un objeto de estado.
  6. Crea un carrito de compras simple.
  7. Bonus: Crea dos componentes (padre e hijo) que compartan estado usando props.