No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Curso de React.js

Curso de React.js

Juan David Castro Gallego

Juan David Castro Gallego

Completando y eliminando TODOs

10/34
Recursos

Aportes 168

Preguntas 9

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

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);
  };

Clase 10 Completando y eliminando TODO’s

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

Para los que quieran poder tambien desmarcar un ToDo completado, puedes asignarle el operador de negación al valor completed, de esta forma se le asignara el valor contrario al estado actual dentro de la función onComplete de esta forma: `newTodos[todoIndex].completed = !newTodos[todoIndex].completed;`

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>
);
}
}
Comparto mi solución al reto con una ternaria. ```js function TodoCounter({ total, completed }) { const title = total == completed ? 'Felicidades TODOS completados' : `Has completado ${completed} de ${total} TODOS`; return (

{title}

); } ```

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));
  }
Es posible ahorrar el filtrado por texto de los todos enviando directamente el index con el segundo parametro del map de la siguiente manera: ```js const completeTodo = (index)=>{ const newTodos = [...todos]; newTodos[index].completed = true; setTodos(newTodos); } <TodoList> { searchedTodos.map((todo, index)=>{ return <TodoItem key={index} text={value.text} completed={value.completed} onCompleted={()=>{completeTodo(index)}}/> }) } </TodoList> ```
Reto del TodoCounter ![](https://static.platzi.com/media/user_upload/image-bde1e300-d245-4ef1-a5de-09d22a75ae69.jpg)

Les comparto la documentación del método .splice por si quieren revisarlo:
Array.prototype.splice()

Estoy Confundido. En términos de "optimización". Es mejor editar/eliminar un elemento de una DB buscándolo por el indéx `(findIndex)` y editándolo? O usando un `map` que retorne la modificación? Gracias Gracias
Esta fue mi solución al problema de cambio de texto cuando se completa el todo ![](https://static.platzi.com/media/user_upload/image-08a02aaa-2033-48e8-911d-275bce65e0c3.jpg)
Aquí una forma más corta de hacer lo mismo en los métodos complete y delete. Complete: `const handleComplete = (text) => {` ` setTodos(` ` todos.map((todo) =>` ` todo.text === text ? { ...todo, completed: !todo.completed } : todo,` ` ),` ` );` ` };` Delete: ` const handleDelete = (text) => {` ` setTodos(todos.filter((todo) => todo.text !== text));` ` };`
2 notas. \- Por temas de SEO o accesibilidad es mejor usar \<button> en lugar de \ para interacciones. \- Usar arrow functions en los atributos onClick va a recrear dichas funciones con cada render. Es mejor solo pasar la referencia de una función.
Para el ejercicio cree la funcion texto inicial que si dectecta que los completados son igual al total me imprime un texto diferente ![](https://static.platzi.com/media/user_upload/image-a1cd9692-aeed-4797-ad6d-0118893bdc0f.jpg)
🟢 Una alternativa a la función `deleteTodo` que nos enseña el profe Juan. Donde se usa el método `toSpliced` que retorna un array, y así no realizar una mutación del estado global `todos` ```js function deleteTodo(text){ const todoIndex = todos.findIndex(todo => todo.text === text); setTodos(todos.toSpliced(todoIndex,1)); } ```
Para los que quieran poder desmarcar un ToDo completado , pueden asignarle la negación del estado actual del valor completed de esta forma ``` newTodos\[todoIndex].completed = !newTodos\[todoIndex].completed; ```
Les comparto cómo resolví el delete del todo. Personalmente sí me gusta utilizar IDs para lo que son datos, por eso tomo el ID como parámetro, pero podría ser reemplazado por el texto como en la clase, sin problemas: ```js const deleteTodo = (id) => { const todosCopy = todos.filter((todo) => todo.id != id); setTodos(todosCopy); }; ```
```js import React from "react"; import '../assets/css/TodoCounter.css'; function TodoCounter(props) { let content; if (props.completed === props.total){ content = <h1 className="TodoCounter">Has completado todos los TODOs felicitaciones ☆ }else{ content = <h1 className="TodoCounter">Has completado {props.completed} de {props.total} TODOs } return ( content ); } export { TodoCounter }; ```no se si sea lo mas adecuado pero ahi esta mi solucion a el ultimo reto
Les comparto mi solución al reto: ```js import './TodoCounter.css'; function TodoCounter({ total, completed }) { const message = (() => { switch (completed) { case 0: return 'No has completado ningún TODO'; case total: return '¡Felicidades! Has completado todos tus TODOs'; default: return <>Has completado {completed} de {total} TODOs; } })(); return ( <h1 className='TodoCounter'> {message} ); } export { TodoCounter }; ```
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 }; 
Reto: Cambio del componente TODOCounter:```js function TodoCounter({ total, completed }) { const allTasksCompleted = completed === total; return ( <h1 className="todoCounter"> {allTasksCompleted && " ✨¡Felicidades has completado todas sus tareas 🎉!"} {!allTasksCompleted &&`Has completado ${completed} de ${total} TodoCounter`} ); } export {TodoCounter}; ```
Este es mi progreso hasta la clase de hoy. haciendo el trabajito en casa :D ![](https://static.platzi.com/media/user_upload/image-1fb00baa-2249-4f32-932e-bc4659e38d89.jpg)
Mi solución al reto: Lo único que hice fue hacer una evaluación el componente TodoCounter.js ```js function TodoCounter({ total, completed }) { if (completed != total) { return ( <h1 className="TodoCounter"> Has completado {completed} de {total} TODOs ) } else { return ( <h1 className="TodoCounter"> 🎉🎉 Felicidades, haz completado todos los TODOS 🎉🎉 ) } } ```
Hey amigos quiero compatir mi code del titulo cuando se completan las tareas: ```js if(completedTodos === totalTodos){ titles = `Well done, all good up to date`; }else{ titles = `You have completed ${completedTodos} of ${totalTodos} to do`; } ```function TodoCounter({titles}){  *return*(    \

      {titles}    \

  );```js <TodoCounter titles ={titles} /* completed={completedTodos} total={totalTodos} *//> ``````js function TodoCounter({titles}){ return(

{titles}

); ```
Yo lo hice diferente pero me gusto tu manera!

El reto lo resolví de la siguiente manera

Son cuatro mensajes posibles

  1. Cuando no hay tareas
  2. Cuando no has completado ninguna tarea
  3. cuando todas las tareas están completadas
  4. El mensaje que ya se tenía
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>
    );
}
Cambie un poco la lógica del `onComplete`, cambie el nombre de la función y en vez de enviarle `true`, le cambio el valor que tenia, con eso podemos marcar como completada o pendiente, dependiendo del estado inicial ```js const handleChangeToDo = (text:string):void => { const newToDos = [...toDos]; const toDoIndex = newToDos.findIndex( (toDo) => toDo.text === text ) newToDos[toDoIndex].completed = !newToDos[toDoIndex].completed; setToDos(newToDos); } ```const handleChangeToDo = (text:string):void => {    const newToDos = \[...toDos];    const toDoIndex = newToDos.findIndex(      (toDo) => toDo.text === text    )    newToDos\[toDoIndex].completed  = !newToDos\[toDoIndex].completed;    setToDos(newToDos);  }
### Operador `delete` + método `splice()` ### ![](https://static.platzi.com/media/user_upload/x-e9d4aca6-1b55-4128-9301-4f2e0e9175ea.jpg)
yo tenia este codigo antes de ver que la de el profe era mas optima. O no se si da igual const todoValues = searchTodos.map((todo, i, array) => { const completFuncion = () => { console.log(`se ejecuta el prodo para completear le todo`) array\[i].complete = true setTodos(searchTodos) } const elimitFuncion = () => { console.log(`se ejecuta el prodo para eliminar un todo`) array.splice(i, 1) setTodos(searchTodos) } return ( \<TodoItem key={todo.text} text={todo.text} complete={todo.complete} completarTodo={completFuncion} eliminarTodo={elimitFuncion} /> ) })
Esta fue mi forma de realizar la función de eliminar un Todo  const deleteTodo = (*text*) => {    const todoDelete = todos.filter(*todo* => *todo*.text !== *text*)    setTodos(todoDelete)  }
Esta ha sido mi solución ![](https://static.platzi.com/media/user_upload/Diapositiva1-321b2b79-68b0-488c-aacc-f2f035c3d429.jpg)![](https://static.platzi.com/media/user_upload/Diapositiva2-05a931db-f6da-4fca-9868-5decb505755d.jpg)
Para los colegas que quieran tachar o quitar el tachado a su todo, este es el codigo   `const completeTodo = (text) => {    const newTodos = [...todos];    const todoIndex = newTodos.findIndex(      (todo) => todo.text === text    );    newTodos[todoIndex].completed = !newTodos[todoIndex].completed;    setTodos(newTodos);  }`
````js const completeTodo = (text) => { const newTodos = [...todos]; const todoIndex = newTodos.findIndex( (todo) => todo.text === text ); newTodos[todoIndex].completed = !newTodos[todoIndex].completed; setTodos(newTodos); } ```  const completeTodo = (text) => {    const newTodos = \[...todos];    const todoIndex = newTodos.findIndex(      (todo) => todo.text === text    );    newTodos\[todoIndex].completed = !newTodos\[todoIndex].completed;    setTodos(newTodos);  } ````
El reto lo resolví de la siguiente manera: import './TodoCounter.css'function TodoCounter({total, completed}){    return(      \<h1 className='TodoCounter'>        {completed=== total        ? 'Lo lograste, completaste todas tus tareas'        : (          <>            Has completado \{completed}\ de \{total}\          \        )}      \    );  }  export {TodoCounter};```js import './TodoCounter.css' function TodoCounter({total, completed}){ return( <h1 className='TodoCounter'> {completed=== total ? 'Lo lograste, completaste todas tus tareas' : ( <> Has completado {completed} de {total} )} ); } export {TodoCounter}; ```
El reto lo resolví de la siguiente manera: ```js import './TodoCounter.css' function TodoCounter({total, completed}){ return( <h1 className='TodoCounter'> {completed=== total ? 'Lo lograste, completaste todas tus tareas' : ( <> Has completado {completed} de {total} )} ); } export {TodoCounter}; ```import './TodoCounter.css'function TodoCounter({total, completed}){    return(      \<h1 className='TodoCounter'>        {completed=== total        ? 'Lo lograste, completaste todas tus tareas'        : (          <>            Has completado \{completed}\ de \{total}\          \        )}      \    );  }  export {TodoCounter};

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.

  1. en caso de que no existan tareas.
  2. en caso de completar todas las tareas.
  3. en caso de que existan tareas pendientes.
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.

Para volver a marcar como pendiente una tarea que ya fue completada

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.

Para cambiar el TotalCounter de manera dinámica

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

Eliminar todo

  function deleteTodo(id){
    // copia del todo, menos del eliminado
    const temp = todos.filter(item => item.id!==id);
    setTodos(temp);
}

marcar todo´s como completado

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:

  • Aquí el prof uso los templates literarios para la validación de la clase completed, pero se puede usar sin ellos.
  • Pero acá es necesario usarlas porque la variable se declara dentro de un string.

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>
Hola! quiero compartirles un proyecto personal de una tienda online para un restaurante link guthub <https://github.com/AlejoG1996/Dtravesia_menuPage> link deploy <https://alejog1996.github.io/Dtravesia_menuPage/> me encnataria recibir sus comentarios o observaciones