React Todo APP
En esta sección realizaremos una práctica para ir aterrizando todo lo aprendido.
⭐ Videos Premium ⭐
Esta sección es parte del curso en Udemy. Si quieres acceder a ella, puedes comprar el curso en Udemy: React + Firebase by bluuweb.
Recursos
App.jsx
jsx
import { useState } from "react";
import Formulario from "./components/Controlado";
import Todos from "./components/Todos";
const initialStateTodos = [
{
id: 1,
title: "Todo #01",
description: "Descripción #01",
state: "pendiente",
priority: true,
},
{
id: 2,
title: "Todo #02",
description: "Descripción #02",
state: "completado",
priority: false,
},
];
const App = () => {
const [todos, setTodos] = useState(initialStateTodos);
const addTodo = (todo) => {
setTodos([...todos, todo]);
};
return (
<div className="container">
<h1>Formularios</h1>
<Formulario addTodo={addTodo} />
<Todos todos={todos} />
</div>
);
};
export default App;
Todos.jsx
jsx
const Todos = ({ todos }) => {
return (
<main>
<h2>Todos</h2>
<ul>
{todos.map((todo) => (
<li key={todo.id}>
<h3>{todo.title}</h3>
<p>{todo.description}</p>
<p>{todo.state ? "Completado" : "Pendiente"}</p>
<p>{todo.priority ? "Prioridad" : "Sin prioridad"}</p>
</li>
))}
</ul>
</main>
);
};
export default Todos;
Formulario.jsx
jsx
const Controlado = ({ addTodo }) => {
const [todo, setTodo] = useState({
title: "Todo #01",
description: "Descripción #01",
state: "pendiente",
priority: true,
});
const { title, description, state, priority } = todo;
const handleSubmit = (e) => {
e.preventDefault();
console.log(title, description, state);
if (!title.trim() || !description.trim()) {
console.log("Datos incompletos");
return;
}
addTodo({
id: Date.now(),
...todo,
state: state === "completado",
});
};
...
};
SweetAlert2
npm install sweetalert2
jsx
import Swal from "sweetalert2";
jsx
const handleSubmit = (e) => {
e.preventDefault();
console.log(title, description, state);
if (!title.trim() || !description.trim()) {
console.log("Datos incompletos");
Swal.fire({
title: "Error!",
text: "Título y descripción son obligatorios",
icon: "error",
});
return;
}
addTodo({
id: Date.now(),
...todo,
state: state === "completado",
});
Swal.fire({
position: "center",
icon: "success",
title: "Tarea agregada con éxito",
showConfirmButton: false,
timer: 1500,
});
};
Todos.jsx
jsx
import Todo from "./Todo";
const Todos = ({ todos }) => {
return (
<main>
<h2 className="text-center my-5">Todos</h2>
<ul className="list-group">
{todos.map((todo) => (
<Todo
key={todo.id}
todo={todo}
/>
))}
</ul>
</main>
);
};
export default Todos;
Todo.jsx
jsx
const Todo = ({ todo }) => {
const { title, description, state, priority } = todo;
return (
<li className="list-group-item d-flex justify-content-between align-items-start">
<div>
<h5 className={`${state && "text-decoration-line-through"}`}>
{title}
</h5>
<p>{description}</p>
<div className="d-flex gap-2">
<button className="btn btn-sm btn-danger">Eliminar</button>
<button className="btn btn-sm btn-info">Actualizar</button>
</div>
</div>
<span className="badge bg-primary rounden-pill">
{priority && "Prioridad"}
</span>
</li>
);
};
export default Todo;
Delete Todo
App.jsx
jsx
const deleteTodo = (id) => {
const newTodos = todos.filter((todo) => todo.id !== id);
setTodos(newTodos);
};
return (
<div className="container mb-5">
<h1 className="my-5">Formularios</h1>
<Formulario addTodo={addTodo} />
<Todos
todos={todos}
deleteTodo={deleteTodo}
/>
</div>
);
Todos.jsx
jsx
import Todo from "./Todo";
const Todos = ({ todos, deleteTodo }) => {
return (
<main>
<h2 className="text-center my-5">Todos</h2>
<ul className="list-group">
{todos.map((todo) => (
<Todo
key={todo.id}
todo={todo}
deleteTodo={deleteTodo}
/>
))}
</ul>
</main>
);
};
export default Todos;
Todo.jsx
jsx
const Todo = ({ todo, deleteTodo }) => {
const { title, description, state, priority } = todo;
return (
<li className="list-group-item d-flex justify-content-between align-items-start">
<div>
<h5 className={`${state && "text-decoration-line-through"}`}>
{title}
</h5>
<p>{description}</p>
<div className="d-flex gap-2">
<button
className="btn btn-sm btn-danger"
onClick={() => deleteTodo(todo.id)}
>
Eliminar
</button>
<button className="btn btn-sm btn-info">Actualizar</button>
</div>
</div>
<span className="badge bg-primary rounden-pill">
{priority && "Prioridad"}
</span>
</li>
);
};
export default Todo;
Upadate Todo
jsx
const updateTodo = (id) => {
const newTodos = todos.map((todo) => {
if (todo.id === id) {
todo.state = !todo.state;
}
return todo;
});
setTodos(newTodos);
};
Todo.jsx
jsx
<button
className="btn btn-sm btn-info"
onClick={() => updateTodo(id)}
>
Actualizar
</button>
Order Todo
jsx
const orderTodos = (arrayTodos) => {
//https://stackoverflow.com/questions/17387435/javascript-sort-array-of-objects-by-a-boolean-property
return arrayTodos.sort((a, b) => {
if (a.priority === b.priority) {
return 0;
}
if (a.priority) {
return -1;
}
if (!a.priority) {
return 1;
}
});
};
useEffect(() => {
setTodos(orderTodos(todos));
console.log("useEffect");
}, [todos]);
return (
<div className="container mb-5">
<h1 className="my-5">Formularios</h1>
<Formulario addTodo={addTodo} />
<Todos
todos={orderTodos(todos)}
deleteTodo={deleteTodo}
updateTodo={updateTodo}
/>
</div>
);
LocalStorage
jsx
const initialStateTodos = JSON.parse(localStorage.getItem("todos")) || [];
const App = () => {
const [todos, setTodos] = useState(initialStateTodos);
// Solo utilizamos un useEffect para actualizar LocalStorage
useEffect(() => {
localStorage.setItem("todos", JSON.stringify(todos));
}, [todos]);
Deploy
npm run build