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.