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 132

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 馃槂

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

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

Clase 10 Completando y eliminando TODO鈥檚

Lo primero en hacer es completar los TODO鈥檚, 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鈥檚, 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

Yo hice que la funci贸n para completar me permita hacer un 鈥渢oggle鈥 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);
	};

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

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

No es el mejor modal, pero funciona. Page: <https://do-it-josttme.vercel.app/> GitHub Code: <https://github.com/josttme/to-do-app> ![](https://res.cloudinary.com/josttme1/image/upload/v1705512670/fbzg4uvazofbcxbrpab7.gif) * Instalar confeti: ```js npm i canvas-confetti -E ``` * Usar confeti:```js import confetti from 'canvas-confetti' // Lamar confetti() ``` Aprendido de MiduDev: <https://midu.dev/>

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

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}

); } ```

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=鈥楾odoCounter鈥>
Has completado <span>{completed}</span> de <span>{total}</span> TODOs
</h1>
);
} else{
return (
<h1 className=鈥楾odoCounter鈥>
Has finalizado los TODOs!
</h1>
);
}
}

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

As铆 hice esta el reto.

馃煝 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; ```
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;`
Reto del TodoCounter ![](https://static.platzi.com/media/user_upload/image-bde1e300-d245-4ef1-a5de-09d22a75ae69.jpg)
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 }; ```

Reto completado 馃槂

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 && " 鉁elicidades has completado todas sus tareas 馃帀!"} {!allTasksCompleted &&`Has completado ${completed} de ${total} TodoCounter`} ); } export {TodoCounter}; ```
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> ```
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!

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

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=鈥淭odoCounter鈥>
Completastes todos los TODOs 馃巿馃巿
</h1>
);
} else {
return (
<h1 className=鈥淭odoCounter鈥>
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);
  };

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

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 鈥溌elicidades, 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=鈥淭odoCounter鈥>
Has resuelto <span>{completed}</span> de <span>{total}</span> todo
</h1>
)}
else { return (
<h1 className=鈥淭odoCounter鈥>
Felicitaciones! No tienes tareas pendientes
</h1>)}
}

export{TodoCounter}

Yo le agregue un nueva 鈥渇uncionalidad鈥 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 鈥渂uena 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鈥spero el prof Juan lo aclar茅 alg煤n d铆a cuando lea mi aporte.

Otra cosa es para los 铆conos鈥ecuerdo 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>
Les comparto como hice el reto: ```jsx import "./TodoCounter.css" function TodoCounter ({total, completed}){ if (total === completed){ return ( <h1 className="TodoCounter">隆Felicitaciones! Has completado todas tus tareas ); } else{ return( <h1 className="TodoCounter">Has completado {completed} de {total} TODOs )} } export {TodoCounter} ```
Debido a que la funci贸n ".find" se toma m谩s tiempo que si tuvi茅ramos el 铆ndice, yo lo hice de esta manera, ya incluyendo el 铆ndice: ```jsx const onCompleteHandler = (todoIndex) => { const newTodos = [...todos]; newTodos[todoIndex].completed = !newTodos[todoIndex].completed; setTodos(newTodos); } ``` 驴Y de d贸nde saco el 铆ndice te preguntar谩s? Pues la funci贸n ".map", con la que renderizamos los items nos permite extraer el 铆ndice (en mi ejemplo llamado "i") como uno de los par谩metros:(no uso los signos de la etiqueta porque platzi me los est谩 quitando) ```tsx filteredTodos.map(({ text, completed }, i) => ( <TodoItem key={i} index={i} text={text} isCompleted={completed} onComplete={() => { onCompleteHandler(i) }} /> )) ``` Y no hay necesidad de disminuir el rendimiento con una b煤squeda.
Yo escribi el codigo igual a Juan, y funciona pero al momento de eliminar todos lo elimina del marcador pero no el todo visual
Logr茅 completar los retos y adem谩s de eso, agregu茅 una funcion que me permite descarcar las tareas que marqu茅 como completadas por accidente. 1\. Para el reto de mostrar un texto diferente cuando est\[an todos los TODOs completado, lo que hice fue crear un if dentro del componente de TodoCounter 2\. Para el tema de desmarcar los elementos, solo cambie la dorma de esignar el valor a la variable, de `newTodos[todoIndex].completed=true` por `newTodos[todoIndex].completed= !newTodos[todoIndex].completed` 3\. Para agregar los iconos de "check", us茅 font awesome, importando el script en el archivo index.html y trayendo el 铆cono mediante una etiqueta \