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)

jsx
Copy
Download
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:

js
Copy
Download
{
  id: 123456789,        // Identificador único
  title: "Ir al médico", // Texto de la tarea
  completed: false       // Estado de completado (true/false)
}
  • completed es 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.

js
Copy
Download
function handleCheckboxChange(id, status) {
  setTodos(todos.map((item) => 
    item.id === id ? { ...item, completed: status } : item
  ));
}

📌 ¿Qué hace este código?

  1. todos.map(...): Crea un nuevo array mapeando todas las tareas.

  2. item.id === id: Busca la tarea con el id que coincide.

  3. { ...item, completed: status }:

    • Copia todas las propiedades de la tarea (...item).

    • Actualiza solo completed con el nuevo status (true/false).

  4. : item: Mantiene las demás tareas sin cambios.

  5. 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>:

jsx
Copy
Download
<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 id ya está asociado a cada tarea.


4. Implementación en el Componente <Todo>

En Todo.js, agregamos un checkbox que usa onComplete:

jsx
Copy
Download
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:

  1. El usuario marca/desmarca el checkbox → onChange se dispara.

  2. e.target.checked contiene el nuevo estado (true/false).

  3. Llama a onComplete (que ejecuta handleCheckboxChange en TodoApp).

  4. React actualiza la UI con el nuevo estado.


5. Feedback Visual para Tareas Completadas

Podemos mejorar la UI para reflejar el estado:

css
Copy
Download
/* En todoApp.css */
.todo.completed {
  opacity: 0.7;
}

.todo.completed span {
  text-decoration: line-through;
  color: #888;
}
  • Esto aplica estilos diferentes cuando item.completed es true.


🎯 Diagrama de Flujo del Estado

text
Copy
Download
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

  1. Persistencia en localStorage:

    js
    Copy
    Download
    useEffect(() => {
      const savedTodos = localStorage.getItem("todos");
      if (savedTodos) setTodos(JSON.parse(savedTodos));
    }, []);
    
    useEffect(() => {
      localStorage.setItem("todos", JSON.stringify(todos));
    }, [todos]);
  2. Contador de tareas completadas:

    jsx
    Copy
    Download
    <div>
      Tareas completadas: {todos.filter((t) => t.completed).length} / {todos.length}
    </div>
  3. Animación al marcar como completado:
    Usar librerías como Framer Motion para animar el cambio.


🚀 Conclusión

  • Usamos map para actualizar el estado sin mutar el array original.

  • El checkbox controla el estado completed de 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

  1. handleCheckboxChange actualiza el estado completed de una tarea usando map.

  2. Comunicación entre componentes mediante props (onComplete).

  3. Renderizado condicional para mostrar tareas completadas (ej: texto tachado).

  4. 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:

javascript
Copy
Download
function handleCheckboxChange(id, status) {
  const updatedTodos = todos.map(item => 
    item.id === id 
      ? { ...item, completed: status }  // ¡Inmutabilidad correcta!
      : item
  );
  setTodos(updatedTodos);
}

Qué hace:

  1. Crea un nuevo array con map: Recorre todas las tareas

  2. Identifica la tarea objetivo: Compara IDs para encontrar el item a modificar

  3. Clona y actualiza inmutablmente: Usa spread operator para crear un NUEVO objeto con la propiedad completed actualizada

  4. Mantiene los demás items intactos: Devuelve los items no modificados directamente

  5. Actualiza el estado: Reemplaza el array completo con la nueva versión


🧩 El Rompecabezas de Componentes

Flujo completo:

  1. TodoApp (padre) define la lógica
    Solo él puede modificar todos porque es el dueño del estado

  2. Pasa la función como prop
    Al renderizar cada Todo, le entrega la capacidad de reportar cambios:

    jsx
    Copy
    Download
    <Todo 
      onComplete={handleCheckboxChange} 
      /* otras props */
    />
  3. Todo (hijo) captura la interacción
    Cuando el usuario cambia el checkbox:

    jsx
    Copy
    Download
    <input
      type="checkbox"
      onChange={(e) => onComplete(item.id, e.target.checked)}
    />
  4. Comunicación ascendente
    El hijo llama a onComplete con:

    • id: Identificador único de la tarea

    • status: Nuevo valor booleano del checkbox (true/false)

  5. TodoApp procesa 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:

javascript
Copy
Download
// ❌ ¡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:

  1. Copia superficial[...todos] duplica el array pero NO los objetos dentro

  2. Mutación de estadoitem.completed = status modifica el objeto original

  3. Problemas de rendimiento: React puede no detectar cambios correctamente

  4. Bugs sutiles: Componentes dependientes podrían no actualizarse

Solución ✅:
Siempre crea nuevos objetos al actualizar estado anidado:

javascript
Copy
Download
{ ...item, completed: status }  // ¡Nuevo objeto!

💡 Patrón Clave: Levantamiento de Estado (State Lifting)

ResponsabilidadTodoApp (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:

jsx
Copy
Download
<span style={{
  color: item.completed ? "#ccc" : "",
  textDecoration: item.completed ? "line-through" : ""
}}>
  {item.title}
</span>

¡Magia de React!
Al actualizarse todos en el padre:

  1. El componente Todo recibe nuevas props

  2. React re-renderiza automáticamente

  3. Los estilos se actualizan visualizando estado completado


🚀 Por qué este Patrón Gana Escala

  1. Centralización: Toda modificación de estado en un solo lugar

  2. Componentes reutilizablesTodo no sabe ni le importa cómo se almacenan las tareas

  3. Mantenimiento simple: La lógica de actualización siempre en el padre

  4. 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

Entradas más populares de este blog

1-Sistema de Tareas en React

0-Gestión de estado en una aplicación de tareas

2-Agregar una Tarea a tu Lista de Tareas en React