6-Manejar el Estado de Tareas
Manejar el Estado de Tareas Completadas en React: Explicación Detallada
En este post, vamos a explorar cómo implementar y entender el funcionamiento del cambio de estado entre tareas completadas y no completadas en nuestra aplicación de lista de tareas usando React.
📌 Código Completo (Con Manejo de Estado de Completado)
import { useState } from "react";
import Todo from "./todo";
import "./todoApp.css";
export default function TodoApp() {
// Estados principales
const [title, setTitle] = useState("");
const [todos, setTodos] = useState([]);
// Función para agregar nueva tarea
function handleSubmit(e) {
e.preventDefault();
const newTodo = {
id: Date.now(),
title: title,
completed: false, // Por defecto, una nueva tarea no está completada
};
setTodos([newTodo, ...todos]);
setTitle("");
}
// Función para eliminar una tarea
function handleDelete(id) {
setTodos(todos.filter((item) => item.id !== id));
}
// Función para actualizar el texto de una tarea
function handleUpdate(id, newTitle) {
setTodos(todos.map((item) =>
item.id === id ? { ...item, title: newTitle } : item
));
}
// Función para cambiar el estado de completado
function handleCheckboxChange(id, status) {
setTodos(todos.map((item) =>
item.id === id ? { ...item, completed: status } : item
));
}
return (
<div className="todoContainer">
<form onSubmit={handleSubmit} className="todoCreateForm">
<input
onChange={(e) => setTitle(e.target.value)}
value={title}
className="todoInput"
placeholder="Escribe una nueva tarea"
/>
<input type="submit" value="Crear tarea" className="buttonCreate" />
</form>
<div className="todosContainer">
{todos.map((item) => (
<Todo
key={item.id}
item={item}
onDelete={() => handleDelete(item.id)}
onUpdate={(newTitle) => handleUpdate(item.id, newTitle)}
onComplete={(status) => handleCheckboxChange(item.id, status)}
/>
))}
</div>
</div>
);
}🔍 Explicación Paso a Paso
1. Estructura del Estado de Cada Tarea
Cada tarea es un objeto con tres propiedades:
{
id: 123456789, // Identificador único
title: "Ir al médico", // Texto de la tarea
completed: false // Estado de completado (true/false)
}completedes un booleano que controla si la tarea está marcada como completada.
2. Función handleCheckboxChange(id, status)
Esta función se encarga de actualizar el estado completed de una tarea específica.
function handleCheckboxChange(id, status) {
setTodos(todos.map((item) =>
item.id === id ? { ...item, completed: status } : item
));
}📌 ¿Qué hace este código?
todos.map(...): Crea un nuevo array mapeando todas las tareas.item.id === id: Busca la tarea con elidque coincide.{ ...item, completed: status }:Copia todas las propiedades de la tarea (
...item).Actualiza solo
completedcon el nuevostatus(true/false).
: item: Mantiene las demás tareas sin cambios.setTodos: Actualiza el estado global con el nuevo array.
3. Pasando la Función al Componente <Todo>
Desde TodoApp, pasamos onComplete como prop a cada <Todo>:
<Todo
key={item.id}
item={item}
onDelete={() => handleDelete(item.id)}
onUpdate={(newTitle) => handleUpdate(item.id, newTitle)}
onComplete={(status) => handleCheckboxChange(item.id, status)}
/>📌 ¿Por qué (status) => handleCheckboxChange(item.id, status)?
Permite pasar el nuevo estado (
status) desde el componente hijo (<Todo>) al padre (TodoApp).El
idya está asociado a cada tarea.
4. Implementación en el Componente <Todo>
En Todo.js, agregamos un checkbox que usa onComplete:
export default function Todo({ item, onDelete, onUpdate, onComplete }) {
const [isEditing, setIsEditing] = useState(false);
const [editedTitle, setEditedTitle] = useState(item.title);
return (
<div className={`todo ${item.completed ? "completed" : ""}`}>
{/* Checkbox para marcar como completado */}
<input
type="checkbox"
checked={item.completed}
onChange={(e) => onComplete(e.target.checked)}
/>
{/* Mostrar texto o input de edición */}
{isEditing ? (
<input
value={editedTitle}
onChange={(e) => setEditedTitle(e.target.value)}
onBlur={() => {
onUpdate(editedTitle);
setIsEditing(false);
}}
autoFocus
/>
) : (
<span
style={{ textDecoration: item.completed ? "line-through" : "none" }}
onClick={() => setIsEditing(true)}
>
{item.title}
</span>
)}
{/* Botones de acciones */}
<button onClick={() => onDelete(item.id)}>Eliminar</button>
</div>
);
}📌 Flujo del Checkbox:
El usuario marca/desmarca el checkbox →
onChangese dispara.e.target.checkedcontiene el nuevo estado (true/false).Llama a
onComplete(que ejecutahandleCheckboxChangeenTodoApp).React actualiza la UI con el nuevo estado.
5. Feedback Visual para Tareas Completadas
Podemos mejorar la UI para reflejar el estado:
/* En todoApp.css */
.todo.completed {
opacity: 0.7;
}
.todo.completed span {
text-decoration: line-through;
color: #888;
}Esto aplica estilos diferentes cuando
item.completedestrue.
🎯 Diagrama de Flujo del Estado
1. Checkbox cambia → Dispara `onChange`
2. Llama a `onComplete(e.target.checked)`
3. `handleCheckboxChange` actualiza `todos`
4. React re-renderiza la lista con el nuevo estado
5. El componente `<Todo>` recibe el nuevo `item.completed`
6. Aplica estilos condicionales (ej: tachado)💡 Mejoras Opcionales
Persistencia en
localStorage:useEffect(() => { const savedTodos = localStorage.getItem("todos"); if (savedTodos) setTodos(JSON.parse(savedTodos)); }, []); useEffect(() => { localStorage.setItem("todos", JSON.stringify(todos)); }, [todos]);Contador de tareas completadas:
<div> Tareas completadas: {todos.filter((t) => t.completed).length} / {todos.length} </div>Animación al marcar como completado:
Usar librerías como Framer Motion para animar el cambio.
🚀 Conclusión
Usamos
mappara actualizar el estado sin mutar el array original.El checkbox controla el estado
completedde cada tarea.Pasamos funciones como props para comunicar componentes.
Estilos condicionales mejoran la experiencia de usuario.
Con esto, tu aplicación ahora permite:
✅ Agregar tareas
✅ Editar texto
✅ Eliminar tareas
✅ Marcar como completadas
¡En el próximo post añadiremos filtros y búsqueda!
Resumen de lo Aprendido
handleCheckboxChangeactualiza el estadocompletedde una tarea usandomap.Comunicación entre componentes mediante props (
onComplete).Renderizado condicional para mostrar tareas completadas (ej: texto tachado).
Flujo unidireccional de datos (padre → hijo → padre).
🚀 ¿Qué más te gustaría aprender?
🔍 Filtrar tareas (activas/completadas/todas).
💾 Guardar en localStorage para persistencia.
🎨 Animaciones al marcar/completar tareas.
🌐 Consumir una API para sincronizar con un backend.
¡Déjame saber en qué más puedo ayudarte! Siempre estoy aquí para profundizar en cualquier tema. ¡Happy coding! 💻🎉
¿Siguiente paso? ¡Podríamos crear un post sobre cómo implementar filtros avanzados o drag-and-drop para reorganizar tareas!
___________________________________________________________
Actualizaciones de Estado con Eventos: La Función handleCheckboxChange
¡Hola comunidad de React! 👋 Hoy desglosaremos una función esencial en cualquier aplicación de tareas: cómo manejar cambios en checkboxes usando el patrón de levantamiento de estado (state lifting). Vamos a explorar handleCheckboxChange y su viaje entre componentes.
🔍 La Función handleCheckboxChange - Versión Mejorada
Primero, optimicemos la función para seguir mejores prácticas de inmutabilidad:
function handleCheckboxChange(id, status) {
const updatedTodos = todos.map(item =>
item.id === id
? { ...item, completed: status } // ¡Inmutabilidad correcta!
: item
);
setTodos(updatedTodos);
}Qué hace:
Crea un nuevo array con
map: Recorre todas las tareasIdentifica la tarea objetivo: Compara IDs para encontrar el item a modificar
Clona y actualiza inmutablmente: Usa spread operator para crear un NUEVO objeto con la propiedad
completedactualizadaMantiene los demás items intactos: Devuelve los items no modificados directamente
Actualiza el estado: Reemplaza el array completo con la nueva versión
🧩 El Rompecabezas de Componentes
Flujo completo:
TodoApp(padre) define la lógica
Solo él puede modificartodosporque es el dueño del estadoPasa la función como prop
Al renderizar cadaTodo, le entrega la capacidad de reportar cambios:<Todo onComplete={handleCheckboxChange} /* otras props */ />Todo(hijo) captura la interacción
Cuando el usuario cambia el checkbox:<input type="checkbox" onChange={(e) => onComplete(item.id, e.target.checked)} />Comunicación ascendente
El hijo llama aonCompletecon:id: Identificador único de la tareastatus: Nuevo valor booleano del checkbox (true/false)
TodoAppprocesa el cambio
Actualiza el estado de manera inmutable y dispara un re-render
⚡ Visualizando el Flujo de Eventos
🚫 Por qué NO Mutar Estado Directamente (como en la versión original)
La implementación original:
// ❌ ¡Cuidado con esta aproximación!
const temp = [...todos]; // Copia superficial
const item = temp.find(item => item.id === id);
item.completed = status; // MUTACIÓN DIRECTA
setTodos([...temp]);Problemas:
Copia superficial:
[...todos]duplica el array pero NO los objetos dentroMutación de estado:
item.completed = statusmodifica el objeto originalProblemas de rendimiento: React puede no detectar cambios correctamente
Bugs sutiles: Componentes dependientes podrían no actualizarse
Solución ✅:
Siempre crea nuevos objetos al actualizar estado anidado:
{ ...item, completed: status } // ¡Nuevo objeto!💡 Patrón Clave: Levantamiento de Estado (State Lifting)
| Responsabilidad | TodoApp (Padre) | Todo (Hijo) |
|---|---|---|
| Almacenamiento estado | ✅ Posee todos | ❌ Solo recibe su item |
| Lógica modificación | ✅ Define handlers | ❌ Solo ejecuta función padre |
| Captura de eventos | ❌ | ✅ Detecta cambios en UI |
| Renderizado condicional | ❌ | ✅ Muestra ✔ según completed |
🌈 Ejemplo en UI: Tachado de Tareas
El componente Todo aplica estilos dinámicos usando el estado completed:
<span style={{
color: item.completed ? "#ccc" : "",
textDecoration: item.completed ? "line-through" : ""
}}>
{item.title}
</span>¡Magia de React!
Al actualizarse todos en el padre:
El componente
Todorecibe nuevas propsReact re-renderiza automáticamente
Los estilos se actualizan visualizando estado completado
🚀 Por qué este Patrón Gana Escala
Centralización: Toda modificación de estado en un solo lugar
Componentes reutilizables:
Todono sabe ni le importa cómo se almacenan las tareasMantenimiento simple: La lógica de actualización siempre en el padre
Optimización: Fácil implementar memoización con
React.memo
✨ Conclusión: Flujo Unidireccional en Acción
handleCheckboxChange ejemplifica el corazón de React:
Datos fluyen hacia abajo (props a
Todo)Eventos fluyen hacia arriba (el hijo ejecuta funciones del padre)
Actualizaciones inmutables garantizan rendimiento y consistencia
¡Dominar este patrón te prepara para Redux, Context API y otras soluciones avanzadas de manejo de estado

Comentarios
Publicar un comentario