Todo App
En esta sección realizaremos una práctica para ir aterrizando todo lo aprendido.
Recursos
En la sección usaremos los siguientes recursos:
Formulario
Vamos a comenzar diseñando la estructura de la aplicación y nuestros primeros componentes, tendremos los siguientes componentes: - App.jsx => Será el componente principal de la aplicación, donde vamos a llamar a los demás. - Formulario.jsx => Formulario de añadir tareas. - TodoList.jsx => Lista de tareas. - Todo.jsx => Tarea.
La lógica vamos a ponerla en el componente que envuelve a todos los demás, App.jsx, con el objetivo de pasarle los estados y funciones al resto de los componentes mediante propps.
App.jsx
import React, { useEffect, useState } from 'react'
import Formulario from './components/Formulario'
import TodoList from './components/TodoList'
const todosInitialState = JSON.parse(localStorage.getItem("todos")) || [ ]
const todoInitialState = {
title: '',
description: '',
state: '',
priority: false
}
const App = () => {
const [todos, setTodos] = useState(todosInitialState)
const [editionMode, setEditionMode] = useState(false)
const [todo, setTodo] = useState(todoInitialState)
useEffect(()=> {
localStorage.setItem("todos", JSON.stringify(todos))
})
const addTodo = todo => {
setTodos([...todos,todo])
}
const deleteTodo = id => {
const newArray = todos.filter(todo => todo.id !== id)
setTodos(newArray)
}
const updateTodoState = id => {
const newArray = todos.map(todo => {
if (todo.id == id) {
todo.state = !todo.state
}
return todo
})
setTodos(newArray)
}
const handleEditionMode = todo => {
setEditionMode(true)
setTodo(todo)
}
const editTodo = id => {
const newArray = todos.map(item => {
if (item.id === todo.id) {
item = todo
}
return item
})
setTodos(newArray)
setEditionMode(false)
setTodo(todoInitialState)
}
return (
<div className='container mt-4'>
<h1 className='text-center'>Todo App</h1>
<hr />
<div className='row mt-2'>
< Formulario
todo={todo}
setTodo={setTodo}
addTodo = {addTodo}
editionMode={editionMode}
editTodo = {editTodo} />
< TodoList
todos={todos}
deleteTodo={deleteTodo}
updateTodoState={updateTodoState}
handleEditionMode={handleEditionMode}
/>
</div>
</div>
)
}
export default App
Formulario.jsx
import { useState } from "react"
import Swal from "sweetalert2"
const Formulario = ({addTodo, editionMode, editTodo, todo, setTodo}) => {
const {title, description, priority, state} = todo
const handleSubmit = e => {
e.preventDefault()
if (title.trim() === "" || description.trim() === "") {
return Swal.fire({
icon: "error",
title: "Oops...",
text: "Algo va mal...!",
});
}
addTodo({
...todo,
id:Date.now(),
state: state == "completada"
})
console.log(`Enviando ${todo.title}, ${todo.description} y ${todo.state} al servidor...`)
}
const handleEdit = e => {
e.preventDefault()
editTodo()
}
const handlechange = e => {
const {name, type, checked, value } = e.target
setTodo({
...todo,
[name]:type === "checkbox"? checked:value
})
}
return (
<div className='col-4'>
<h3 className='text-center'>{editionMode ? 'Editar Tarea' : 'Agregar tareas'}</h3>
<form onSubmit={editionMode ? handleEdit : handleSubmit}>
<input
name="title"
placeholder="Introduce nombre de la tarea"
type="text"
className="form-control mb-2"
value = {todo.title}
onChange={handlechange}
/>
<textarea
name="description"
placeholder="Introduce la descripcion"
className="form-control mb-2"
value = {todo.description}
onChange={handlechange}
/>
<select
name="state"
className="form-control mb-2"
value = {todo.state}
onChange={handlechange}
>
<option value="pendiente">Pendiente</option>
<option value="completada">Completada</option>
</select>
<div className="form-checked mb-2">
<input
className="form-checked-input"
type="checkbox"
name="priority"
id = "inputchecked"
checked = {todo.priority}
onChange={handlechange}
/>
<label
htmlFor="inputchecked"
className="form-checked-label"
>
Prioridad
</label>
</div>
{editionMode ? (
<button className='btn btn-warning w-100 mt-2'>
Guardar Cambios
</button>
) : (
<button className='btn btn-dark w-100 mt-2'>Agregar</button>
)}
</form>
</div>
)
}
export default Formulario
Todolist.jsx
import React from 'react'
import Todo from './Todo'
const TodoList = ({todos, deleteTodo, updateTodoState, handleEditionMode}) => {
return (
<div className='col-8'>
<h3 className='text-center'>Lista de tareas</h3>
<ul>
{
todos.sort((a,b) => {
return (b.priority - a.priority) - (b.state - a.state)*2
})
.map(todo=> (
< Todo
key={todo.id}
todo={todo}
deleteTodo={deleteTodo}
updateTodoState={updateTodoState}
handleEditionMode={handleEditionMode}
/>
))
}
{
todos.length === 0 && (
<li className='list-group-item text-center'>No hay tareas pendientes</li>
)
}
</ul>
</div>
)
}
export default TodoList
Todo.jsx
import React from 'react'
const Todo = ({todo, deleteTodo, updateTodoState, handleEditionMode}) => {
const {id, title, description, priority, state} = todo
return (
<li className='list-group-item'>
<div className="d-flex justify-content-between align-items-start">
<div>
<h5 className={state ? 'completada' : undefined}>
{title}
</h5>
<p className={state ? 'completada' : undefined}>
{description}
</p>
<p className={state ? 'completada' : undefined}> </p>
<div className='d-flex'>
<button onClick={() => deleteTodo(id)} className='btn btn-sm btn-danger mr-2'>Eliminar</button>
<button onClick={() => handleEditionMode(todo)} className='btn btn-sm btn-warning mr-2'>Editar</button>
<button onClick={() => updateTodoState(id)} className='btn btn-sm btn-primary'>Actualizar Estado</button>
</div>
</div>
<span className="badge badge-primary">
{priority && "prioridad"}
</span>
</div>
</li>
)
}
export default Todo
Deploy
Para hacer el deploy en primer lugar generamos los archivos estáticos de la aplicación ejecutando en el terminal del visual code:
npm run build
Una vez ejecutado se nos habrá generado una carpeta, dist, en el directorio, que es la que subimos a nuestro hosting.