La moraleja de esta clase?
Aprender Metodos de Arrays es muy necesario y útil!
Introducción y requisitos
¿Qué necesitas para aprender React.js?
Maquetación con React.js
¿Qué es un componente?
Componentes de TODO Machine
¿Cómo se comunican los componentes? Props y atributos
Estilos CSS en React
Interacción con React.js
Eventos en React: onClick, onChange
¿Qué es el estado?
Contando TODOs
Buscando TODOs
Completando y eliminando TODOs
Librería de Iconos Personalizados
Iconos en React: librerías y SVG
Iconos con colores dinámicos
Herramientas avanzadas: escalabilidad, organización y persistencia
Local Storage con React.js
Custom Hooks
Organización de archivos y carpetas
Feature-First Directories en React
Tips para naming y abstracción de componentes React
¿Qué son los efectos en React?
Estados de carga y error
Actualizando estados desde useEffect
Reto: loading skeletons
¿Qué es React Context?
useContext
¿Qué son los React Portals?
Reto: estados para abrir y cerrar un modal
Maquetando formularios en React
Crear TODOs: React Context dentro de React Portals
Deploy
Despliegue de TODO Machine en GitHub Pages
Presentación de proyectos para tu portafolio
Próximos pasos: React #UnderTheHood
Diferencias entre versiones de React.js
¿Cuándo realmente necesitas React.js?
Bonus: creando proyectos en React desde cero
React con Create React App
React con Next.js
React con Vite
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Aportes 168
Preguntas 9
La moraleja de esta clase?
Aprender Metodos de Arrays es muy necesario y útil!
Para marcar como complete el todo agregue para poder marcarlo y desmarcarlo
const onComplete = (text) => {
const todoIndex = todos.findIndex((todo) => todo.text === text);
const newTodos = [...todos];
newTodos[todoIndex].completed = !newTodos[todoIndex].completed;
setTodos(newTodos);
};
Y para eliminar los todos, lo hice con filter
const onDelete = (text) => {
const newTodos = todos.filter((todo) => todo.text != text);
setTodos(newTodos);
};
Les dejo mi solución del reto para dar otro titulo si todas las tareas fueron completadas 😃
Les dejo mi solución al reto, decidir usar operadores ternarios.
import './TodoCounter.css';
function TodoCounter({ total, completed }) {
return (
completed === total
? <h1 className='TodoCounter'>Has completado todos tus TODOS 🥳</h1>
:<h1 className='TodoCounter'>Has completado <span> {completed} </span>de <span>{total}</span> TODOS</h1>
);
}
export { TodoCounter };
Para las personas que les sale un warning cuando guardan, es porque el Profe Juan esta utilizando == que significa qu es del mismo valor … y no === que significa igual valor e igual tipo.
En si es por la comparación que se esta realizando.
//Marcando completados los ToDos
const completeTodo = (text) => {
const newTodos = [...todos];
const todoIndex = newTodos.findIndex(
(todo) => todo.text === text
);
newTodos[todoIndex].completed = true;
setTodos(newTodos);
};
Lo primero en hacer es completar los TODO’s, para esto utilizamos el useState de React cambiando el estado del componente con la propiedad completed={todo.completed}
, luego debemos enviarle un actualizador de estado creando un evento propio con onComplete={completeTodo}
al componente todoItem
.
Luego crearemos la constante completeTodo del parametro enviado al componente:
const completeTodo = (text) => {
const newTodos = [...todos];
const todoIndex = newTodos.findIndex(
(todo) => todo.text === text
);
newTodos[todoIndex].completed = true;
setTodos(newTodos);
console.log('Hola llego aquí');
}
Una ves definida la constante como parametro enviamos text
por ser nuestra clave en el caso de los Todo’s, luego usamos la expresión extendida de JS con ...
es decir traeremos todo el array todos
para crear un nuevo array. En la siguiente linea hacemos un filtro por el Index
que definimos para obtener el todo
que estamos buscando. En el nuevo elemento encontrado lo definimos con el estado completed
como true
y definimos el array original con el nuevo, finalmente enviamos un console.log
para verificar que el estado se modifico de acuerdo a nuestra logica.
Ahora para poder definir la ejecución de la función inicialmente utilizamos el siguiente codigo:
<TodoList>
{searchedTodos.map(todo => (
<TodoItem
key={todo.text}
text={todo.text}
completed={todo.completed}
onComplete={completeTodo(todo.text)}
/>
))}
</TodoList>
El problema con esta función es que genera un re render
que rompe la aplicación por su autoejecución dentro del codigo sin tener el parametro text
, por lo tanto lo que se tiene que corregir es:
<TodoList>
{searchedTodos.map(todo => (
<TodoItem
key={todo.text}
text={todo.text}
completed={todo.completed}
onComplete={() => completeTodo(todo.text)}
/>
))}
</TodoList>
Encapsulando la función dentro de otra asi se ejecuta unicamente cuando es encesario y no cuando se renderiza la aplicación.
Hasta esta parte todo bien, ahora toca hacer el metodo delete
, por lo tanto usaremos el mismo codigo de completed
:
<TodoList>
{searchedTodos.map(todo => (
<TodoItem
key={todo.text}
text={todo.text}
completed={todo.completed}
onComplete={() => completeTodo(todo.text)}
onDelete={() => deleteTodo(todo.text)}
/>
))}
</TodoList>
Enviamos la función onDelete
con el texto como parametro, luego definimos la función:
const deleteTodo = (text) => {
const newTodos = [...todos];
const todoIndex = newTodos.findIndex(
(todo) => todo.text === text
);
newTodos.splice(todoIndex, 1)
setTodos(newTodos);
console.log('Hola llego aquí');
}
Y dentro de nuestro componente TodoItem
enviamos la función onClick
:
<span
className="Icon Icon-delete"
onClick={props.onDelete}
>
X
</span>
Si realizamos los pasos tal como estan descritos la aplicación deberia función de forma interactiva!!!
Creo que me emocioné tanto en la clase 5 creando la página que sin querer completé este reto al trabajar con los diálogos de la presentación, las interacciones con las tareas, una especie de tutorial, añadir personajes y… creo que terminé creando un juego envés de una Todo List 😅
Mejor me relajo porque hasta planee los minijuegos y si sigo añadiéndole cosas no termino más esto, haha
Les comparto mi solución al reto, si alguien tiene alguna sugerencia estaré super feliz de leerla c:
function TodoCounter({ total, completed }) {
return (
<>
{total === completed && (
<h2 className="title">¡Felicidades, completaste todos las tareas!</h2>
)}
{total !== completed && (
<h2 className="title">
<span> {completed} </span>
Tareas completadas de
<span> {total} </span>
</h2>
)}
</>
);
}
Yo hice que la función para completar me permita hacer un “toggle” por si nos equivocamos de TODO al completar, o simplemente hacemos clic por error 🤷🏾♂️…
const onComplete = (description) => {
const newTasks = [...tasks];
const tasksIndex = newTasks.findIndex(
(task) => task.description === description
);
newTasks[tasksIndex].isCompleted === true
? (newTasks[tasksIndex].isCompleted = false)
: (newTasks[tasksIndex].isCompleted = true);
setTasks(newTasks);
};
El reto opcional se puede solucionar facilmente usando el operador ternario de esta forma:
function TodoCounter({total, completed }) {
return (
<h2 className="h2-title">{total === completed ? `Felicitaciones` : `Has completado ${completed} de ${total} TODOs`}</h2>
)
}
Peroooooooooooo
Se le puede añadir un poco de dinamimo añadiendo varios mensajes para mostrar en un array y mostrandolos de forma aleatoria de esta forma:
function TodoCounter({total, completed }) {
const Felicitaciones = ["Felicidades! Haz completado tus tareas 😁", "Vaya, Hoy ha sido un dia productivo",
"Te has ganado un descanso de 25 minutos ✋", "Completaste todos tus pendientes 😎", "¿De verdad terminaste todas tus tareas en 1 segundo o solo oprimiste el boton de completado para engañarme? 👀😒"]
const randomIndex = Math.floor(Math.random() * Felicitaciones.length);
return (
<h2 className="h2-title">{total === completed ? `${Felicitaciones[randomIndex]}` : `Has completado ${completed} de ${total} TODOs`}</h2>
)
}
No es la mejor forma de resolver el reto (y probablemente tampoco la mas eficiente).
Así hice esta el reto.
Les dejo mi solucion al reto!!, al comienzo me enrede creando otro archivo y demas pero luego lo solucione de una manera mas sencilla para mi!
function TodoCounter({ total, completed }) {
if (completed != total) {
return (
<h1 className=‘TodoCounter’>
Has completado <span>{completed}</span> de <span>{total}</span> TODOs
</h1>
);
} else{
return (
<h1 className=‘TodoCounter’>
Has finalizado los TODOs!
</h1>
);
}
}
Sobre el reto, en mi caso preferí hacerlo así:
function TodoHeader({ completedTodos, totalTodos }) {
let completionMsg = "";
if (totalTodos === 0) {
completionMsg = "❄️ No hay tareas pendientes";
} else if (completedTodos === totalTodos) {
completionMsg = "🥳 Todas las tareas completadas";
} else {
completionMsg = `Tasks: ${completedTodos} of ${totalTodos} completed`;
}
return (
<div className="header">
<h1>To-Do App</h1>
<p className="TodoCounter">{completionMsg}</p>
</div>
);
}
jajajaja parce!!! le entiendo todo siguiendo la clase, pero como hago para escribir en mis notas todo esto? jajajajajajaja osea no hay forma de explicar en texto para mi yo del futuro que repase este tema de como fue que hicimos todo estoooooooooooooooooo
Reto completado 😃
Por aquí mi solución, decidí encapsular los mensajes para que el return se vea limpio.
import './css/TodoCounter.css';
export function TodoCounter({ total, completed }) {
const completedMsg = `¡Felicidades! ¡Has completado todos tus TODOs!`
const defaultMsg = <>Has completado <span>{completed}</span> de <span>{total}</span> TODOs`</>
return (
<h1 className="TodoCounter">
{completed === total && total !== 0 ? completedMsg :
defaultMsg}
</h1>
)
}
Mis funciones quedaron asi:
const handleCheck = (todoText) => {
setTodos(todos.map(todo => {
if (todo.text === todoText) {
return { ...todo, completed: !todo.completed };
} else {
return todo;
}
}));
}
const handleDelete = (todoText) => {
setTodos(todos.filter(todo => todo.text !== todoText));
}
Les comparto la documentación del método .splice por si quieren revisarlo:
Array.prototype.splice()
import './TodoCounter.css';
function TodoCounter({ totalTodos, completedTodos }) {
return (
<h1 className="TodoCounter">
{completedTodos === totalTodos
? 'Has completado todos tus TODOs!'
: <>Has completado <span>{completedTodos}</span> de <span>{totalTodos}</span> TODOs</>}
</h1>
);
}
export { TodoCounter };
Son cuatro mensajes posibles
function TodoCounter({ total, completed }) {
let message;
if (total === 0) {
message = 'No tienes tareas';
} else if (completed === 0) {
message = 'No has completado ninguna tarea';
} else if (completed === total) {
message = 'Todas las tareas están completadas';
} else {
message = `Has completado ${completed} de ${total} TODOS`;
}
return (
<h1 className="TodoCounter">
{message}
</h1>
);
}
Hola les dejó mi solucción
import ‘./TodoCounter.css’;
function TodoCounter({total, completed}) {
if (total === completed) {
return (
<h1 className=“TodoCounter”>
Completastes todos los TODOs 🎈🎈
</h1>
);
} else {
return (
<h1 className=“TodoCounter”>
Has completado <span>{completed}</span> de <span>{total}</span> TODOs
</h1>
);
}
}
export {TodoCounter};
Yo complete el reto de esta manera
return (
<>
<h2 className={`TodoCounter ${completed == total && "completedTodos"}`}>Has completado <span>{completed}</span> de <span>{total}</span> tareas</h2>
<h2 className={`TodoCounter ${completed < total && "completedTodos"} ${total == 0 && "completedTodos"}`}>Has completdado todas tus Tareas</h2 >
<h2 className={`TodoCounter ${!total == 0 && "completedTodos"}`}>No tienes tareas pendientes</h2 >
</>
)
Esta es otra manera que encontré de hacer el deleteTodo con menos líneas
const deleteTodo = (text) => {
const resultado = todos.filter(todo => todo.text != text)
setTodos(resultado);
};
También le agregué una validación a completeTodo para que se pueda regresar el estado a no completado por si nos arrepentimos
const completeTodo = (text) => {
const newTodos = [...todos];
const todoIndex = newTodos.findIndex(
(todo) => todo.text == text
);
if (newTodos[todoIndex].completed == false) {
newTodos[todoIndex].completed = true;
}else{
newTodos[todoIndex].completed = false;
}
setTodos(newTodos);
};
MI SOLUCIÓN AL RETO!!
function TodoCounter({total, completed}){
return(
completed === total ?
<h1 className="TodoCounter">
Felicidades!! Has completado <span>TODAS</span> las tareas.</h1>
:
<h1 className="TodoCounter">
Has completado <span>{completed}</span> de <span>{total}</span> TODOs
</h1>
);
}
Mi solucion al reto…
Cuando el listado de TODO está vació
Cuando en el listado hay TODOs sin completar
Cuando todos los TODOs estan completos
function TodoCounter({ completed, total }) {
return (
<>
{total === 0 && (
<h1 className='TodoCounter'>
No tienes ningun TODO por hacer...😕
</h1>
)}
{completed !== total && (
<h1 className='TodoCounter'>
Has completado <span>{completed}</span> de{' '}
<span>{total}</span> TODOs
</h1>
)}
{completed === total && total !== 0 && (
<h1 className='TodoCounter'>
Felicidades, has completado el listado de TODOs 😎
</h1>
)}
</>
);
}
Completado
Ya que el profe unicamente puso para marcar los que ya teniamos completado pero nunca para desmarcarlos yo lo hice de esta forma para poder desmarcarlos aplicando un if que validara si tenia true o false.
Espero les sirva…!!!
<const completeTodo = (text) => {
const newTodos = [...todos]
const todoIndex = newTodos.findIndex(
(todo) => todo.text == text
);
if((newTodos[todoIndex].completed) == false){
newTodos[todoIndex].completed = true;
} else if((newTodos[todoIndex].completed)== true) {
newTodos[todoIndex].completed = false;
}
setTodos(newTodos);
}>
¡Hola a todos!
Yo hice mi código un poco distinto, entonces lo comparto por si gustas mirarlo. Es mediante props mediante dos funciones, la de completar, y la de borrar.
const TodoItem = ({todo, index, completed, setDefaultTodo, defaultTodos}) => {
const handleComplete = () => {
defaultTodos[index].completed = !defaultTodos[index].completed;
setDefaultTodo([...defaultTodos])
}
const deleteTodo = () => {
defaultTodos.splice(index, 1)
setDefaultTodo([...defaultTodos])
}
return (
<li className={`${index % 2 === 0 ? "li" : "lili"}`}>
<span onClick={()=>handleComplete()} title={completed ? "Completado" : "Incompleto"}>{completed ? "👍🏼" : "👎🏼"}</span>
<p className={`${completed ? "completed" : ""}`}>{todo}</p>
<span onClick={()=>deleteTodo()} className="span">❌</span>
</li>
);
}
export default TodoItem;
Hay algunas cositas que agregue yo por mi cuenta jaja xd
Saludos!!
<code>
import React from 'react';
import './styles.css';
function TodoCounter({ total, completed }) {
return (
<div>
{total === completed ? (
<h1 className="todo-counter">¡Felicidades, has completado todas tus tareas!</h1>
) : (
<h1 className="todo-counter">Has completado {completed} de {total} TODOS</h1>
)}
</div>
);
}
export default TodoCounter;
RETO FINAL
Aquí el código modificado para mostrar un mensaje distinto si el total es igual a completado:
import React from "react";
import "./TodoCounter.css";
function TodoCounter({ total, completed }) {
let message;
if (total === completed) {
message = "¡Felicidades, has completado todos los TODOs!";
} else {
message = `Has completado ${completed} de ${total} TODOs`;
}
return (
<h1 className="TodoCounter">
{message}
</h1>
);
}
export { TodoCounter };
Ahora, cuando el valor de total
sea igual al valor de completed
, mostrará el mensaje “¡Felicidades, has completado todos los TODOs!”, de lo contrario, mostrará el mensaje original.
precento avance
TodoCounter
import React from 'react';
function TodoCounter({total,completed}) {
return (
<h1 className="complete-list">
{/*- se utiliza un Operador condicional ternario para comparar si la fueron completadas todas las tareas */}
{total === completed
? 'felicidades'
:`Has completado ${completed} de ${total} TODOS` }
</h1>
)
}
export {TodoCounter};
uso de input checkbox
import React from 'react';
function TodoItem(props) {
return (
<li className={`tasks ${props.completed && "tasks-complete"}`}>
<input type='checkbox' className="item-complete" onChange={props.onComplete} checked={props.completed} />
<p className="tasks__text">{props.text}</p>
<div onClick={props.onDelete} className="tasks__remove"><svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 24 24" xmlns=""><path d="m4.015 5.494h-.253c-.413 0-.747-.335-.747-.747s.334-.747.747-.747h5.253v-1c0-.535.474-1 1-1h4c.526 0 1 .465 1 1v1h5.254c.412 0 .746.335.746.747s-.334.747-.746.747h-.254v15.435c0 .591-.448 1.071-1 1.071-2.873 0-11.127 0-14 0-.552 0-1-.48-1-1.071zm14.5 0h-13v15.006h13zm-4.25 2.506c-.414 0-.75.336-.75.75v8.5c0 .414.336.75.75.75s.75-.336.75-.75v-8.5c0-.414-.336-.75-.75-.75zm-4.5 0c-.414 0-.75.336-.75.75v8.5c0 .414.336.75.75.75s.75-.336.75-.75v-8.5c0-.414-.336-.75-.75-.75zm3.75-4v-.5h-3v.5z" className="color--recycle-bin" fill ="#e0e1dd" fill-rule="nonzero"/></svg></div>
</li>
)
}
export {TodoItem}
detectar si el usuario quiere desmarcar la tarea
const completeTodo = (text) => {
const newTodos = [...todos];
const todoIndex = newTodos.findIndex(
(todo) =>todo.text ==text
);
console.log(newTodos[todoIndex]);
//- compara si el input checked fue seleccionado o deseleccionado
newTodos[todoIndex].completed = newTodos[todoIndex].completed
? false
: true
;
setTodos(newTodos);
};
Show diff text on todo
import './TodoCounter.css';
function TodoCounter({ total, completed }) {
return (
<h1 className="TodoCounter">
{ total == completed ? <>
Felicidades completastes todas las tareas
</> :
<>
Has completado <span>{completed}</span> de <span>{total}</span> TODOs
</>
}
</h1>
);
}
export { TodoCounter };
Genial ✔️✅🤯
Les comparto mi solucion del reto adiciional, en la cual devuelvo 3 tipos de resultados.
function TodoCounter({total, completed}){
const sinTareas = (total == 0 && completed == 0);
const iguales = total == completed;
if(sinTareas){
return(
<h1 >
No hay tareas que realizar, agrega una nueva
</h1>
);
}if(iguales){
return (
<h1 >
Felicidades has completado todas tus tareas
</h1>
);
}else{
return(
<h1 >
Has completado {completed} de {total} tareas
</h1>
);
}
}
Reto de felicitaciones resuelto con condicional IF
import “./todocounter.css”
function TodoCounter({total, completed}){
if (total > completed){
return (
<h1 className=“TodoCounter”>
Has resuelto <span>{completed}</span> de <span>{total}</span> todo
</h1>
)}
else { return (
<h1 className=“TodoCounter”>
Felicitaciones! No tienes tareas pendientes
</h1>)}
}
export{TodoCounter}
Yo le agregue un nueva “funcionalidad” la cual permite desmarxar un ToDo en caso de que lo hayamos checkeado por error o algún otro motivo. Lo hice con un operador ternario
//Función para seleccionar un todo como completado
const completeTodo = (text) => {
const newTodos = [...todos]
const todoIndex = newTodos.findIndex((todo) => todo.text === text);
newTodos[todoIndex].completed === true ? newTodos[todoIndex].completed = false : newTodos[todoIndex].completed = true;
setTodos(newTodos)
};
Con esto al volver a dar clic podemos marcar o desmarcar ese todo.
App.js
const completeTodo = (text) => {
const newTodos = [...todos];
const todoIndex = newTodos.findIndex((todo) => todo.text === text);
newTodos[todoIndex].completed
? (newTodos[todoIndex].completed = false)
: (newTodos[todoIndex].completed = true);
setTodos(newTodos);
};
Simplemente se agrega una condicional que valida si es que el value de completed ya se encuentra en true
para actualizar su estado a false
y viceversa.
TotalCounter
de manera dinámicaTodoCounter.js
import "./TodoCounter.css";
function TodoCounter({ total, completed }) {
if (total === 0 && completed === 0) {
return <h1 className="TodoCounter">You haven't created any task yet</h1>;
} else if (total > 0 && completed > 0 && total === completed) {
return (
<h1 className="TodoCounter">
Congratulations! You have completed all of your tasks!
</h1>
);
} else {
return (
<h1 className="TodoCounter">
You have completed <span> {completed} </span> out of
<span> {total} </span> tasks
</h1>
);
}
}
export { TodoCounter };
Otra forma en la que hice poder hacer check o uncheck al item.
const toggleTodo = (todoKeyText) => {
// Iteramos todos los todos hasta encontrar un match con la validación.
const updatedTodos = todos.map(todo => {
// Validación para encontrar el item
if(todo.text === todoKeyText){
return {
// Cuando encuentra un match, regresa el
...todo,
completed: !todo.completed
}
}
return todo;
})
setTodos(updatedTodos);
}
.
<TodoList>
{searchedTodos.map(todo => (
<TodoItem
key={todo.text}
text={todo.text}
completed={todo.completed}
// Funciona como un evento
// Al hacer click se ejecuta la función con los param que enviemos.
toggleTodo={() => toggleTodo(todo.text)}
/>
))}
</TodoList>
.
TodoItem.jsx
.
<button
onClick={toggleTodo}
className="Icon Icon-check">
{completed ? <FiCheckCircle className="Icon-check--active"/> : <FiCircle/> }
</button>
podemos ahorrarnos una parte del codigo si llamamos el parametro index gracias a la función map:
<TodoList>
{searchedTodos.map((todo, index) => (
<TodoItem
key={index}
text={todo.text}
completed={todo.completed}
iteration = { index }
onComplete = {()=>
completeTodo(index)}
/>
))}
</TodoList>
La solución trabajar con el TodoCounter.js de la siguiente manera
simplemente use un ternario en donde si completed es igual al total muestra el mensaje de felicidades con un efecto y si no muestra el progreso
function TodoCounter({ total, completed }) {
return (
<h1 className={completed === total ? 'sparkles' : ''}>
{completed === total ? (
<span>Felicidades, has completado todos!</span>
) : (
<>
<span className="TodoCounter"> Has completado {completed} de {total} Todos</span>
</>
)}
</h1>
);
}
efecto sparkles
@keyframes sparkles-animation {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.2);
opacity: 0.8;
}
100% {
transform: scale(1);
opacity: 1;
}
}
.sparkles {
animation: sparkles-animation 1s infinite;
}
import './TodoCounter.css';
function TodoCounter({completed,total}) {
console.log(completed==total);
return (
<h1 className="todoCounter">{
completed == total
? `Felicidades por completar tus TODOS 🥳`
: `Has Completado ${completed} de ${total} TODOS` }</h1>
)
}
export {TodoCounter}
JuanDc es un verdadero crack explicando
para la solucion del reto no esta estilizado ya que primero maqueto, luego creo toda las funcionalidades y al final estilizo
lo que realice fue usar la libreria de reacticons y en la parte de completed utilice sus iconos check cuando esta completado y de X cuando no esta completado, en la parte de laX en el ejemplo del profesor le coloque un icono de basurero que permite eliminar de la lista.
les dejo la solucion al reto de esta clase lo realice usando un ternario y la validacion de de completed === total, es corta pero efectiva.
function deleteTodo(id){
// copia del todo, menos del eliminado
const temp = todos.filter(item => item.id!==id);
setTodos(temp);
}
function completedTodo(id) {
const todoIndex = todos.findIndex((todo) => todo.id === id);
const temp = [...todos];
temp[todoIndex].completed = !temp[todoIndex].completed; // Alternar el valor de `completed`
setTodos(temp);
}
Esta fue mi solución al reto, utilicé la librería canvas-confetti, al finalizar las tares se muestre la animación
import { confettiFireworks } from './utils/fireworks'
export const TodoCounter = ({total, completed}) => {
let mensaje = `Has completado ${completed} de ${total} TODOS`;
if(completed===total){
mensaje = `¡Felicitaciones!, completaste todas las tareas`;
confettiFireworks();
}
return (
<Typography variant="body2" color="white" align="center">
{mensaje}
</Typography>
)
}
Dejo la referencia de la librería
canvas-confetti pruebenla, está genial
Creando otro componente para el mensaje de completado de tareas
Hola, esta fue mi solución
function TodoCounter({ total, completed }) {
const text = () => {
if (total === 0) {
return `No hay tareas, ¡agrega algunas!`;
}
if (total === completed) {
return `${completed} de ${total}. Todas las fueron tareas completadas 🥳 descansa un poco`;
}
return `Has compltetado ${completed} de ${total} TODOS`;
};
return <h1 className="todoCounter">{text()}</h1>;
}
MI solución fue esta:
import './TodoCounter.css';
const estilos = {
textAlign: 'center'
}
function TodoCounter({total, completed}) {
return (
<>
{completed === total && <h2 style={estilos}>Felicitaciones! Has completado todas las tareas!</h2>}
<h1 className='TodoCounter'>
Has completado <span>{completed}</span> de <span>{total}</span> TODOS
</h1>
</>
);
}
export {TodoCounter};
Buenas compañeros, comparto mi solución. Estoy abierto a cualquier feedback soy totalmente nuevo en react por lo que no sé si usar fragments en diferentes constantes sea “buena practica”.
import "./TodoCounter.css";
function TodoCounter({ total, completed }) {
const defaultTitleContent = <>
Has completado <span>{completed}</span> de <span>{total}</span> TODOS
</>;
const alternativeTitleContent = <>Felicidades! Completaste todo los TODOs!</>;
return (
<h1 className="TodoCounter">
{ total === completed ? alternativeTitleContent : defaultTitleContent }
</h1>
);
}
export { TodoCounter };
Reto:
if(todos.filter(todo => todo.completed).length === todos.length){
alert("Felicidades completastes todos los TODOs")
}
Cree variables para hacer el proceso en app
const completedTodos =todos.filter(todo => !!todo.completed).length
const totalTodos = todos.length;
const textoPerron = completedTodos !== totalTodos ? "Haz completado <span>"+completedTodos+"</span> de <span>"+totalTodos+"</span> TODOs" : "Felicidades completaste tus tareas!"
<TodoCounter
texto = {textoPerron}
/>
recupero en todoCounter
<h1 className="TodoCounter" dangerouslySetInnerHTML={{__html: texto }} />
el reto sería algo así como
import './TodoCounter.css';
function TodoCounter({ total, completed }) {
return (
<h1 className="TodoCounter">
completedTodos === totalTodos ? "Excelente, eres el mejor o la mejor completaste todos los TODOs" : `Has completado ${completed} de ${total} TODOs`
</h1>
);
}
export { TodoCounter };
Agregué un condicional que permite quitar el completado de las tareas
const taskCompleted = (text)=>{ //función para marcar como completada una tarea
const newArrayTask = [...countTask];
const taskIndex = newArrayTask.findIndex((task)=>//encontrar el índice de la tarea donde se hizo clic en check
task.text == text
)
if(!newArrayTask[taskIndex].completed){
newArrayTask[taskIndex].completed=true;
}else{
newArrayTask[taskIndex].completed=false;
}
setCountTask(newArrayTask);
}
Y con respecto al reto mi solución fue hacer un condicional en TodoCounter.js
import "./TodoCounter.css";
function TodoCounter(props) {
return (
<h2>{props.completed == props.total ? "FELICITACIONES" : `Has completado ${props.completed} de ${props.total}`}</h2>
)
}
export {TodoCounter};
Una cosa que he notado es que no es necesario agregarse las comillas invertidas siempre que se use code JS puro en el JSX de los componentes, ya que las variables pueden estar sin ellas. Lo que si es necesario usarlas es cuando las variables van dentro de un string.
Por ejemplo:
No sé si es mala práctica hacer ello o no…espero el prof Juan lo aclaré algún día cuando lea mi aporte.
Otra cosa es para los íconos…recuerdo en una clase del profesor Diego de Granda, si no me equivoco en el curso de HTML Y CSS definitivo, que cuando se desea agregar algún ícono para nuestra aplicación, es recomendado descargarlos, ya que si se usa solo un link, ese link podría fallar y además baja el rendimiento al cargar ese ícono o imagen externo.
Así que yo descargue los íconos de ioicons o flaticon
Y los tengo guardados en una carpeta dentro de src
No sé si este haciendo bien pero así lo estoy haciendo en este proyecto.
Esta es mi solución algo simple pero funciona! 😊
Lo que use fue un** operador ternario**, tuve que buscar en la documentación de react.
Algo que les recomiendo es que vayan a la documentación oficial de React cuando tengan una duda, . (Tambien esta en español! link)
{completed !== total ? (
<h1 className="TodoCounter">
Has completado <span>{completed}</span> de <span>{total}</span> TODOs
</h1>
) : (
<h2 className="TodoCounter">
Felicitaciones! Completaste tus TODOs 🥳 🎉🎉
</h2>
)}
Así quedó resuleto lo del reto:
Y obviamente para hacer el toggle + eliminando con filter:
Comparto mi onComplete y onDelete
const onComplete = (text) => () => {
const newTodos = todos.map(todo => {
if (todo.text === text) todo.completed = !todo.completed;
return todo;
});
setTodos(newTodos);
};
const onDelete = (text) => () => {
const newTodos = todos.filter(todo => todo.text !== text);
setTodos(newTodos);
};
Hago una doble encapsulación de la función para no tener que hacerla en el HTML (JSX) del componente.
para cambiar el texto solamente agregué una validación de los contadores
function ToDoCounter({ total, completed }) {
if (total === completed) {
return (
<h1 className="ToDoCounter">
You have completed all your ToDos... Congratulations!🥳
</h1>
);
}
return (
<h1 className="ToDoCounter">
You have completed <span>{completed}</span> of <span>{total}</span> ToDos
</h1>
);
}
export { ToDoCounter };
Y para los iconos se pueden agregar usando la tecla de windows + . (⌐■_■) te dan emojis y todo lo que quieras escoger
Aqui les dejo mi reto completado!!
import './css/ToDoCounter.css'
function ToDoCounter({ total, completed }){
if (total >0){
return (
<h1>
Haz completado {completed} de {total} tareas
</h1>
);
}else{
return (
<h1>
¡Felicidades haz completado todas las tareas!
</h1>
);
}
}
export { ToDoCounter };
Mi solución:
function TodoCounter({ total, completed }) {
if (total === completed) {
return (
<h1 className="TodoCounter">Felicidades has completado todos tus pendientes</h1>
);
}
return (
<h1 className="TodoCounter">Has completado <span>{completed}
</span> de <span>{total}</span> TODOs
</h1>
); }
export { TodoCounter };
Mi solución al reto:
import './TodoCounter.css';
function TodoCounter({total, completed}) {
const tasksCompleted = <h1 className='TodoCounter'>Has completado todos tus TODOS</h1>;
const tasksNoCompleted = <h1 className='TodoCounter'>Has completado <span>{completed}</span> de <span>{total}</span></h1>;
return (
<>
{total === completed ? tasksCompleted : tasksNoCompleted}
</>
);
}
export { TodoCounter };
No es la mas optima pero, si se necesita realizar una validación mucho mas compleja que una condicional esto podría servir.
Uso un ternario diferente al que explicaron, para mi es mas hermoso a la vista, se separa con los : puntos
const validateAllTaskcomplete = (total, completed) => {
if(total === completed){
return true
}else{
return false
}
}
function TaskCounter({total , completed}){
return (
validateAllTaskcomplete(total , completed) ?
(<h1 className="TaksCounter" >Felicidades completastes todos tus pendientes</h1>)
:
(<h1 className="TaksCounter">Haz completado <span>{completed}</span> de <span>{total}</span> Tareas</h1>)
)
}
Yo resolví el reto opcional de la siguiente manera, aunque siento que no está del todo bien, les dejo el código.
const allTodosCompleted = () => {
return completedTodos === totalTodos;
}
{ allTodosCompleted() ? <h1>Felicidades has completado todos los TODOs 💚</h1> : <TodoCounter completed={completedTodos} total={totalTodos}/>}
Son bienvenidas sus opiniones para mejorar. Gracias.
Yo hice este pequeño cambio para poder marcar y desmarcar los TODOS:
En el Todo Item en lugar de enviar solo el atributo text, mando el todo completo
<TodoItem
key={todo.text}
text={todo.text}
completed={todo.completed}
onComplete={() => completeTodo(todo)}
/>
Y en la funcionalidad hago la desestructuracion del todo y al tocar el icono el todo cambiara propiedad completed al inverso
const completeTodo = ({text, completed}) => {
const newTodos = [...todos]
const todoIndex = newTodos.findIndex(
(todo) => todo.text === text
)
newTodos[todoIndex].completed = !completed
setTodos(newTodos)
}
Agregué esta pequeña condicional. El código si quitabas el check, no se restaba del total.
Este es el icono de check ✓ por si les sirve
me gustaria que profundisen mas al momento de aplicar logicas ,a veces no se entiende y debemos investigar aparte,se entiende que es algo de todos los dias pero me sentiria un poco mas de fluides.
En mi caso separe todo la logica de la renderizacion de todo items a una funcion para tener el componente App un poco mas limpio de la siguiente manera.
function renderTodoItems(todos, setTodos) {
const completeTodo = (text) => {
const newTodos = [...todos]; //Create a copy of the array
const todoIndex = newTodos.findIndex(todo => todo.text === text);
newTodos[todoIndex].completed = !newTodos[todoIndex].completed
setTodos(newTodos);
};
const deleteTodo = (text) => {
const newTodos = [...todos].filter(todo => todo.text !== text);
setTodos(newTodos);
};
return todos.map((todo, index) =>
<TodoItem
key={todo.text}
text={todo.text}
completed={todo.completed}
onCompleted={() => completeTodo(todo.text)}
onDelete={() => deleteTodo(todo.text)} />)
}
En el componente App queda de la siguiente manera
<TodoList>
{
renderTodoItems(searchedTodos, setTodos)
}
</TodoList>
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?