Skip to content

Unidad 5: Gestión de Estado en el Cliente

Objetivo: Introducir patrones para la gestión de datos en aplicaciones dinámicas.


5.1 Uso de event-driven programming en el frontend

¿Qué vamos a aprender?

En esta sección aprenderemos cómo funciona la programación basada en eventos en el frontend, utilizando eventos personalizados con EventEmitter.

Creación y escucha de eventos personalizados

Ejemplo de EventEmitter en JavaScript

class EventEmitter {
    constructor() {
        this.eventos = {};
    }

    on(evento, listener) {
        if (!this.eventos[evento]) {
            this.eventos[evento] = [];
        }
        this.eventos[evento].push(listener);
    }

    emit(evento, data) {
        if (this.eventos[evento]) {
            this.eventos[evento].forEach(listener => listener(data));
        }
    }
}

const emisor = new EventEmitter();

emisor.on("mensaje", (data) => {
    console.log("Evento recibido con datos:", data);
});

emisor.emit("mensaje", "Hola desde EventEmitter!");
Explicación: - on(evento, listener): Registra un listener para un evento. - emit(evento, data): Dispara el evento y ejecuta los listeners asociados.


5.2 Flujo de datos entre distintos componentes

¿Qué vamos a aprender?

Aprenderemos a manejar el flujo de datos entre distintos componentes en el frontend utilizando eventos y el patrón Pub/Sub (Publicador/Suscriptor).

Paso de datos entre componentes con eventos

Ejemplo de paso de datos con eventos personalizados

const eventos = new EventEmitter();

function componente1() {
    eventos.emit("actualizar", "Datos enviados desde Componente 1");
}

function componente2() {
    eventos.on("actualizar", (data) => {
        console.log("Componente 2 recibió:", data);
    });
}

componente2();
componente1();
Explicación: - componente1() emite un evento con datos. - componente2() escucha ese evento y recibe los datos.

Introducción a Pub/Sub en JavaScript

Ejemplo de Pub/Sub en JavaScript

class PubSub {
    constructor() {
        this.suscriptores = {};
    }

    suscribir(evento, callback) {
        if (!this.suscriptores[evento]) {
            this.suscriptores[evento] = [];
        }
        this.suscriptores[evento].push(callback);
    }

    publicar(evento, data) {
        if (this.suscriptores[evento]) {
            this.suscriptores[evento].forEach(callback => callback(data));
        }
    }
}

const pubsub = new PubSub();

pubsub.suscribir("notificacion", (mensaje) => {
    console.log("Notificación recibida:", mensaje);
});

pubsub.publicar("notificacion", "Nuevo mensaje disponible");
Explicación: - suscribir(evento, callback): Permite a los componentes suscribirse a un evento. - publicar(evento, data): Lanza un evento que notifica a los suscriptores.


5.3 Aplicaciones Reales con Event-Driven y Pub/Sub

Caso 1: Carrito de Compras en una Tienda Online

Objetivo: Implementar un carrito de compras dinámico donde los productos añadidos se reflejan instantáneamente en la interfaz sin recargar la página.


Estructura del HTML

Interfaz de la tienda con carrito dinámico

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Tienda Online</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="container mt-4">
    <h2 class="mb-4">Tienda Online</h2>
    <button class="btn btn-primary" id="ver-carrito">Ver Carrito (<span id="contador-carrito">0</span>)</button>

    <div class="row mt-3" id="productos">
        <div class="col-md-4">
            <div class="card">
                <div class="card-body">
                    <h5 class="card-title">Producto 1</h5>
                    <button class="btn btn-success agregar-carrito" data-producto="Producto 1">Agregar</button>
                </div>
            </div>
        </div>
        <div class="col-md-4">
            <div class="card">
                <div class="card-body">
                    <h5 class="card-title">Producto 2</h5>
                    <button class="btn btn-success agregar-carrito" data-producto="Producto 2">Agregar</button>
                </div>
            </div>
        </div>
    </div>

    <script src="carrito.js"></script>
</body>
</html>

Código en JavaScript (carrito.js)

Gestión del carrito con eventos

class EventEmitter {
    constructor() {
        this.eventos = {};
    }

    on(evento, listener) {
        if (!this.eventos[evento]) {
            this.eventos[evento] = [];
        }
        this.eventos[evento].push(listener);
    }

    emit(evento, data) {
        if (this.eventos[evento]) {
            this.eventos[evento].forEach(listener => listener(data));
        }
    }
}

const eventos = new EventEmitter();
let carrito = [];

document.querySelectorAll(".agregar-carrito").forEach(boton => {
    boton.addEventListener("click", (event) => {
        let producto = event.target.dataset.producto;
        carrito.push(producto);
        eventos.emit("actualizarCarrito", carrito.length);
    });
});

eventos.on("actualizarCarrito", (cantidad) => {
    document.getElementById("contador-carrito").textContent = cantidad;
});

Explicación: - Cada botón de producto emite un evento cuando se agrega al carrito. - El carrito se actualiza en tiempo real sin necesidad de recargar la página. - Se muestra un contador con la cantidad de productos en el carrito.


Caso 2: Aplicación de Tareas Dinámicas (To-Do List)

Objetivo: Implementar una aplicación de tareas en la que se puedan añadir y eliminar tareas sin recargar la página.


Estructura del HTML

Interfaz de la aplicación de tareas

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Lista de Tareas</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="container mt-4">
    <h2>Lista de Tareas</h2>
    <form id="form-tarea">
        <input type="text" id="nueva-tarea" class="form-control" placeholder="Escribe una tarea" required>
        <button type="submit" class="btn btn-primary mt-2">Agregar</button>
    </form>
    <ul class="list-group mt-3" id="lista-tareas"></ul>

    <script src="tareas.js"></script>
</body>
</html>

Código en JavaScript (tareas.js)

Gestión de tareas con eventos

class PubSub {
    constructor() {
        this.suscriptores = {};
    }

    suscribir(evento, callback) {
        if (!this.suscriptores[evento]) {
            this.suscriptores[evento] = [];
        }
        this.suscriptores[evento].push(callback);
    }

    publicar(evento, data) {
        if (this.suscriptores[evento]) {
            this.suscriptores[evento].forEach(callback => callback(data));
        }
    }
}

const pubsub = new PubSub();

document.getElementById("form-tarea").addEventListener("submit", (event) => {
    event.preventDefault();
    let tarea = document.getElementById("nueva-tarea").value;
    pubsub.publicar("nuevaTarea", tarea);
    document.getElementById("nueva-tarea").value = "";
});

pubsub.suscribir("nuevaTarea", (tarea) => {
    let lista = document.getElementById("lista-tareas");
    let li = document.createElement("li");
    li.className = "list-group-item d-flex justify-content-between";
    li.innerHTML = `${tarea} <button class="btn btn-danger btn-sm">X</button>`;
    li.querySelector("button").addEventListener("click", () => {
        li.remove();
    });
    lista.appendChild(li);
});

Explicación: - Cada tarea nueva es publicada en el sistema Pub/Sub. - Los suscriptores (la lista de tareas) reciben el evento y actualizan la interfaz. - Las tareas pueden eliminarse sin recargar la página.