Implementación de un Gestor de Tareas con Redux Toolkit y React
1. Configuración del Estado Global: taskSlice.js
Definición del slice para gestionar las tareas mediante Redux Toolkit, incorporando generador de identificadores únicos con nanoid.
import { createSlice, nanoid } from '@reduxjs/toolkit';
const taskSlice = createSlice({
name: 'tasks',
initialState: [],
reducers: {
addTask: (state, action) => {
// action.payload trae { nombre, descripcion }
// AGREGA LA NUEVA TAREA AL ARREGLO DE ESTADO (Usando nanoid() para el id)
state.push({
id: nanoid(),
nombre: action.payload.nombre,
descripcion: action.payload.descripcion
});
},
deleteTask: (state, action) => {
// action.payload trae el id de la tarea a eliminar
// REGRESA EL ESTADO SIN ESA TAREA
return state.filter(task => task.id !== action.payload);
},
clearTasks: (state) => {
// VACÍA POR COMPLETO LA LISTA DE TAREAS
return [];
},
},
});
export const { addTask, deleteTask, clearTasks } = taskSlice.actions;
export default taskSlice.reducer;2. Componentes de la Aplicación
Formulario de Tareas: TaskForm.jsx
Componente controlado para la captura y despacho de nuevas tareas hacia el store de Redux.
import { useState } from 'react';
import { useDispatch } from 'react-redux';
import { addTask } from '../redux/taskSlice';
export default function TaskForm() {
// DECLARA EL ESTADO LOCAL PARA NOMBRE Y DESCRIPCION
const [nombre, setNombre] = useState('');
const [descripcion, setDescripcion] = useState('');
const dispatch = useDispatch();
const handleSubmit = () => {
// DISPARA addTask CON LOS DATOS DEL FORMULARIO Y LIMPIA LOS CAMPOS (Validando que no estén vacíos)
if (nombre.trim() === '' || descripcion.trim() === '') {
alert('Todos los campos son obligatorios');
return;
}
dispatch(addTask({ nombre, descripcion }));
setNombre('');
setDescripcion('');
};
return (
<div>
{/* INPUTS CONTROLADOS CONECTADOS AL ESTADO LOCAL */}
<input
type="text"
placeholder="Nombre de la tarea"
value={nombre}
onChange={(e) => setNombre(e.target.value)}
/>
<textarea
placeholder="Descripción de la tarea"
value={descripcion}
onChange={(e) => setDescripcion(e.target.value)}
/>
<button onClick={handleSubmit}>Agregar tarea</button>
</div>
);
}Lista de Tareas: TaskList.jsx
Recupera la lista de tareas del estado global mediante el hook useSelector y mapea cada elemento.
import { useSelector } from 'react-redux';
import TaskItem from './TaskItem';
export default function TaskList() {
// OBTÉN EL ARREGLO DE TAREAS DESDE EL STORE
const tasks = useSelector(state => state.tasks);
return (
<div>
{/* RECORRE LAS TAREAS Y RENDERIZA UN <TaskItem /> PASÁNDOLE LAS PROPS */}
{tasks.map(task => (
<TaskItem
key={task.id}
id={task.id}
nombre={task.nombre}
descripcion={task.descripcion}
/>
))}
</div>
);
}Elemento Individual: TaskItem.jsx
Presenta la información de una tarea puntual y expone la opción de eliminarla disparando la acción deleteTask.
import { useDispatch } from 'react-redux';
import { deleteTask } from '../redux/taskSlice';
// SE DECLARAN LAS PROPS QUE RECIBE EL COMPONENTE (id, nombre, descripcion)
export default function TaskItem({ id, nombre, descripcion }) {
const dispatch = useDispatch();
return (
<div>
{/* MUESTRA EL NOMBRE Y LA DESCRIPCIÓN RECIBIDOS POR PROPS */}
<h3>{nombre}</h3>
<p>{descripcion}</p>
{/* DISPARA deleteTask USANDO EL ID RECIBIDO POR PROPS */}
<button onClick={() => dispatch(deleteTask(id))}>
Eliminar
</button>
</div>
);
}Página Principal: Page.jsx
Conecta la estructura principal al proveedor de contexto Provider y añade la acción global para vaciar el listado.
import { Provider } from 'react-redux';
import { store } from '../redux/store';
import TaskForm from '../components/TaskForm';
import TaskList from '../components/TaskList';
// IMPORTA clearTasks PARA EL BOTÓN DE "Eliminar todas"
import { clearTasks } from '../redux/taskSlice';
export default function Page() {
return (
<Provider store={store}>
{/* ARMA LA PÁGINA USANDO TaskForm, TaskList Y UN BOTÓN QUE DISPARE clearTasks() */}
<div>
<TaskForm />
<TaskList />
<button onClick={() => store.dispatch(clearTasks())}>
Eliminar todas
</button>
</div>
</Provider>
);
}3. Respuestas de la Trivia para Puntos Extra
Para asegurar todos los puntos extra, aquí tienes las respuestas correctas de la trivia:
- ¿Cuándo es el cumpleaños de Humberto? (Esta parece ser una broma interna de la clase, así que deja la respuesta que ya pusiste o la que sepas que el profesor espera).
- ¿Nombre del primer Hokage? Hashirama Senju.
- Menciona 3 integrantes de La Casa de los Famosos de la misma temporada: Wendy Guevara, Poncho de Nigris y Nicola Porcella (Temporada 1, México).
- Da 4 vueltas corriendo...: ¡A hacer ejercicio físico!
- ¿Cuántos océanos existen? (Nómbralos): Existen 5 océanos: Pacífico, Atlántico, Índico, Antártico (o Austral) y Ártico.
- Menciona 6 Pokémon: Pikachu, Charizard, Bulbasaur, Squirtle, Gengar y Mewtwo.