No tienes acceso a esta clase

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

Aprende todo un fin de semana sin pagar una suscripción 🔥

Aprende todo un fin de semana sin pagar una suscripción 🔥

Regístrate

Comienza en:

4D
21H
44M
23S
Curso de Introducción a React.js

Curso de Introducción a React.js

Juan David Castro Gallego

Juan David Castro Gallego

Completando y eliminando TODOs

11/23
Recursos

Para crear las funcionalidades de completar y eliminar TODOs podemos crear una función que reciba el id o texto de nuestro TODO, para después editarlo o eliminarlo.

Completando TODOs

Creamos la función completeTodo, que recibirá el texto de nuestro TODO, ubicamos el TODO en nuestro arreglo, cambiamos el valor de la propiedad completed de nuestro TODO y muy importante actualizar nuestro estado, para que React pueda re-renderizar nuestros TODOs con los nuevos datos.

const completeTodo = (text) => {
    const todoIndex = todos.findIndex(todo => todo.text === text);
    const newTodos = [...todos];
    newTodos[todoIndex].completed = true;
    setTodos(newTodos);
  };

Eliminando TODOs

Podemos hacer algo parecido a la función de completar, pero ahora, en lugar de cambiar si está completada o no, solamente la eliminaremos de nuestros TODOs con el método splice, y también regresaremos un nuevo arreglo con los TODOs actualizados.

const deleteTodo = (text) => {
    const todoIndex = todos.findIndex(todo => todo.text === text);
    const newTodos = [...todos];
    newTodos.splice(todoIndex, 1);
    setTodos(newTodos);
  };

App.js

Una vez tenemos creada la lógica para completar y eliminar TODOs, podemos pasar esas funciones a nuestros TodoItem.

import React from 'react';
import { TodoCounter } from './TodoCounter';
import { TodoSearch } from './TodoSearch';
import { TodoList } from './TodoList';
import { TodoItem } from './TodoItem';
import { CreateTodoButton } from './CreateTodoButton';
// import './App.css';

const defaultTodos = [
 { text: 'Cortar cebolla', completed: true },
 { text: 'Tomar el cursso de intro a React', completed: false },
 { text: 'Llorar con la llorona', completed: true },
 { text: 'LALALALAA', completed: false },
];

function App() {
 const [todos, setTodos] = React.useState(defaultTodos);
 const [searchValue, setSearchValue] = React.useState('');

 const completedTodos = todos.filter(todo => !!todo.completed).length;
 const totalTodos = todos.length;

 let searchedTodos = [];

 if (!searchValue.length >= 1) {
   searchedTodos = todos;
 } else {
   searchedTodos = todos.filter(todo => {
     const todoText = todo.text.toLowerCase();
     const searchText = searchValue.toLowerCase();
     return todoText.includes(searchText);
   });
 }

 const completeTodo = (text) => {
   const todoIndex = todos.findIndex(todo => todo.text === text);
   const newTodos = [...todos];
   newTodos[todoIndex].completed = true;
   setTodos(newTodos);
 };

 const deleteTodo = (text) => {
   const todoIndex = todos.findIndex(todo => todo.text === text);
   const newTodos = [...todos];
   newTodos.splice(todoIndex, 1);
   setTodos(newTodos);
 };
 
 return (
   
     
     

     
       {searchedTodos.map(todo => (
          completeTodo(todo.text)}
           onDelete={() => deleteTodo(todo.text)}
         />
       ))}
     

     
   
 );
}

export default App;

TodoItem.js

Para que nuestra aplicación funcione también tenemos que recibir las props en nuestros ítems y usarlas.

import React from 'react';
import './TodoItem.css';

function TodoItem(props) {
  return (
    <li className="TodoItem">
      <span
        className={`Icon Icon-check ${props.completed && 'Icon-check--active'}`}
        onClick={props.onComplete}
      >span>
      <p className={`TodoItem-p ${props.completed && 'TodoItem-p--complete'}`}>
        {props.text}
      p>
      <span
        className="Icon Icon-delete"
        onClick={props.onDelete}
      >
        X
      span>
    li>
  );
}

export { TodoItem };

Contribución creada por: Brandon Argel.

Aportes 98

Preguntas 38

Ordenar por:

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

o inicia sesión.

Por favor, un curso intermedio y uno avanzado con este mismo profe. Es muy simple y claro a la vez.

Yo me tome el atrevimiento y cambie el método completeTodo() por toggleCompleteTodo() para permitir que destilde de completado algún TODO que le marco completado sin querer. De esta forma sigue funcionando todo y me parece más genial para el usuario por si se confunde.
.
La solución me quedo asi:

const toggleCompleteTodos = (text) => {
	const todoIndex = todos.findIndex(todo => todo.text === text);
	const newTodos = [...todos];
	newTodos[todoIndex].completed = !newTodos[todoIndex].completed;
	setTodos(newTodos);
}

Hasta ahora, excelente curso BatiProfe 😃

Otra forma de borrar los todos

function deleteTodo(text){
    const newTodos = todos.filter(todo=>todo.text !== text)
    setTodos(newTodos)
  }

La mejor frase que puedo escuchar de Juan es decir: "Entonces, recapitulemos…"
Todos los profesores deberían hacer esto para poder juntar toda la información en nuestro cerebro de forma más fácil

Para cambiar el icono de la tarea es muy sencillo:

Mandamos por props una propiedad donde mandemos por valor todo.complete (es el mapeo del nuevo array que se creo la clase pasada de toda la información de los todos)

Y en el componente TodoItem traer ese prop y hacer un if ternario como en la siguiente imagen y listo! Espero haber ayudado 😃

Así va quedando mi Todo-Machine 🔥


Le añadí un botón para editar Todos 🦾

  • Luego lo mejorare con un Modal


.

Porfa que el curso de avanzado lo dicte JUAN DAVID los otros profesores son buenos pero hablan como si uno ya supierta todo en cambio Juan te lleva de la mano y sin darte cuenta estás entiendiendolo todo!

me tome el atrevimiento de hacer una verificacion. Cuando un todo no este completado no se pueda eliminar!

necesitamos mas cursos como este

Hola, trate de optimizar el código y permitir al usuario cambiar entre completo o no. Quedó así:

  const findIndex = (text) => {
    return todos.findIndex(todo => todo.text === text)
  }

  const toggleTodo = (text) => {

    const newTodos = [...todos]
    newTodos[findIndex(text)].completed = !newTodos[findIndex(text)].completed
    setTodos(newTodos)
  }

  const deleteTodo = (text) => {

    const newTodos = [...todos]
    newTodos.splice(findIndex(text), 1)
    setTodos(newTodos)
  }

Por favor, un curso intermedio y uno avanzado con este mismo profe. Es muy simple y claro a la vez.
Apoyo la porpuesta de @Marcelo Diaz. De verdad muy buen profe

Yo originalmente ya había usado un índice en la TodoList:

<TodoList>
        {searchedTodos.map((todo, index) => (
          <TodoItem
            text={todo.text}
            key={index}
            completed={todo.completed}
            onComplete={() => completeTodo(index)}
            onDelete={() => deleteTodo(index)}
          />
        ))}
</TodoList>

Por lo que en vez de buscar por texto (ya con esto podemos tener 2 o más elementos con mismo texto) podemos buscar por índice:

const completeTodo = (index) => {
    const newTodos = [...todos]
    newTodos[index].completed = !newTodos[index].completed
    setTodos(newTodos)
  }

  const deleteTodo = (index) => {
    const newTodos = [...todos]
    newTodos.splice(index, 1)
    setTodos(newTodos)
  }

Les dejo una muestra de los avances que tengo hasta el momento 🥳

Les comparto mi progreso con un diseño con una paleta de colores similar a la de Platzi 😄
.

Les comparto otra alternativa de completeTodo y deleteTodo…
En este caso todo sucede dentro del setState para ahorrar memoria en la aplicación.
Cuando manejas una app o una página que actualiza listas muy grandes te permitirá ahorrar memoria al no tener que crear nuevas variables con la lista a modificar.

/**
 * TOGGLE TODO COMPLETED/UNCOMPLETED
 * 
 * @param {text} text 
 */
const toggleCompleteTodo = (text) => {
  //* Update the state of todos with setTodos
  setTodos(
    //* Filter the todos array
    todos.map(todo => {
      //* If the text of the todo matches the text of the todo that was clicked
      if (todo.text === text) {
        return {
          //* ... Include the rest of the todo array
          ...todo,
          //* And toggle the completed property
          completed: !todo.completed,
        };
      }
      //* Return the rest of the todos array unchanged
      return todo;
    })
  );
};

/**
 * DELETE TODO
 * 
 * @param {text} text 
 */
const deleteTodo = (text) => {
  //* Update the state of todos with setTodos
  setTodos(todos.filter(
    //* Filter the todos array to remove the todo that was clicked
    todo => todo.text !== text
  ));
};

Preferi implementar un toggleTodo que un todoComplete por si hacemos click por error.

newTodos[todoIdx].completed = !newTodos[todoIdx].completed;

Yo le pase los todos y el setTodo al item y desde ahi resolvi la logica de la siguiente forma, pudiendo marcar y desmarcar el todo por si lo marcaste por error y aun no lo habias completado, yo mismo hice los svg para no utilizar iconos y/o imagenes

function TodoItem({text, completed, todos, setTodos}) {
  const onComplete = () => {
    let actualizarTodos = [] 
    if(completed) {
      actualizarTodos = todos.map(item => {
        if(text === item.text) {
          item.completed = false;
        }
        return item;
      })
    } else {
      actualizarTodos = todos.map(item => {
        if(text === item.text) {
          item.completed = true;
        }
        return item;
      })
    }
    setTodos(actualizarTodos);
  }
  const onDelete = () => {
    setTodos(todos.filter(item => item.text !== text))
  }

  return (
    <li className="TodoItem">
      <svg 
        className={`iconCheck ${completed && 'iconCheck--active'}`}
        height="40"
        width="40"
        onClick={onComplete}
      >
       <path d="M 25 13 A 10 10 0 1 0 30 20 m 0 0 z M 15 20 l 5 5 L 33 10 " />
      </svg>
      <p className={`TodoItemText ${completed && 'TodoItemText--completed'}`}>{text}</p>
      <svg
        className={`iconDump ${completed && 'iconDump--active'}`}
        height="40"
        width="40"
        onClick={onDelete}
      >
       <path d="M 8 10 L 32 10 m 0 0 z M15 10 l 0 -3 l 2 -2 l 6 0 l 2 2 l 0 3 z" />
       <path d="M10 15 l 20 0 l -3 20 l -14 0 l -3 -20" />
       <path d="M15 18 l 1 14 z M 20 18 l 0 14 z M 25 18 l -1 14" />
      </svg>
    </li>
  )
}

export { TodoItem };

Algo útil, sería permitirle al usuario poder quitar el to-do como completado. Para hacer esto, basta con colocar

newTodos[todoIndex].completed = ! newTodos[todoIndex].completed

El cual servirá de toggler, donde si se hace click y está marcado como completado, lo desmarcará y quedará sin completar.

con un IF en la funcion completeTodos de App.js, puedes volver a destachar un TODO tachado(algo básico, pero por si lo necesitan:

const completeTodos = (text) => {
    const todoIndex = todos.findIndex(todo => todo.text === text);
    const newTodos = [...todos];
    if(todos[todoIndex].completed === false){
      todos[todoIndex].completed = true;
    } else{
      todos[todoIndex].completed = false;
    }
    setTodos(newTodos);
  };

Otra alternativa de resolver el mismo problema:

const completeTodos = text => {
setTodos(prev => prev.map(todo => todo.text === text
		? {...todo, completed: true}
		: todo
	))
}
const deleteTodos = text => {
	setTodos(prev => prev.filter(todo => todo.text !== text))
}

Completando TODOS

La funcion recibe el texto, lo ubica en el array y su propiedad completed cambia el valor, actualizamos el estado para que React re-renderize los TODOS con los nuevos datos.

Eliminando TODOs
Ahora en lugar de cambiar el estado del TODO eliminaremos usando el metodo splice y regresaremos un nuevo array actualizado con los TODOs.

Para que sirva la app tenemos que recibir las props en nuestros items y usarlas

Es mas fácil así, solo le pasas el índice y ya ¯_(ツ)_/¯:

(task, i) => <TodoItem ... onCheck={() => updateItemCheck(i)} />

Para la función de deleteTodo yo no la hice con splice, la hice con filter:

const deleteTodo(text) => {
  const removeTodo = todos.filter((todo) => todo.text !== text);
  setTodos(leftTodos);
}

Para quienes no les funcione, cambie el nombre de la propiedad onComplete por complete y onDelete por delete (cambiarlo por otro, pero con on lo probre varias veces y no me funciono) , me funciono de esta manera.

<TodoList>

        {searchTodos.map(todo => (          
          <TodoItem 
            key={todo.text} 
            text={todo.text}
            completed={todo.completed}
            complete={() => completeTodo(todo.text)}
            delete={() => deleteTodo(todo.text)}
          />
        ))}    

Hice lo mismo que el compañero lucas y de verdad que le agrega mucha funcionalidad a la app por si nos llegamos a equivocar y marcamos una tarea como completada cuando aún no está completa🥴

Aquí dejo el código:
En App.js

En TodoItem.Js

Con esto, marcamos y desmarcamos los todo.

    newTodos[todoIndex] = {
      text:       newTodos[todoIndex].text,
      completed:  !newTodos[todoIndex].completed
    }
    

Para que al momento de darle click en el chulo de completarTodo, se podria poner la negacion de el valor del todo por si quisiera indicar nuevamente que no se ha completado:

newTodos[indexTodo].completed = !newTodos[indexTodo].completed;

Me pareció interesante poder hacer que el ícono de check funcione como un toggle (un botón que se activa y desactiva) por lo que agregúe un condicional dentro de nuestra función:

const toggleCompleteTodos = (text) =>
  {
    const todoIndex = todos.findIndex(task=> task.text == text);
    const newTodos = [...todos];
    if(newTodos[todoIndex].completed == false) // Si nuestro valor es falso, lo cambiamos a true
    {
      newTodos[todoIndex].completed = true;
    }
    else // caso contrario, va false :)
    {
      newTodos[todoIndex].completed = false;
    }
    setTodos(newTodos);
  }

Never stop learning ♥

Una cosa interesante que me paso antes de ver la resolución del video fue la de clonar el vector todos, no se si sera algo trivial pero cuento lo que me paso por si alguno le sirve.

//en mi caso la llame así  y recibe la posicion del vector
 const finishTodo = (id) => {
        todos[id].completed = true;
        let aux = todos;
        setTodos(aux);
    };

si hacia esta asignación

let aux = todos;

no se realizaba el cambio de estado en la pagina de forma automática
en cambio al hacerlo como el profesor si funcionaba instantáneamente.

let aux = [...todos];

Investigando un poco, creo que esto se debe a la forma en que js copia los arrays. De la anterior forma se copiaba una referencia y de la segunda se hacia una copia real del array.
Dejo una guia en donde lo explican de mejor manera
https://www.kuworking.com/javascript-como-copiar-arrays

//permite completar y descompletar un todo
  const completeTodos = (text) => {
    setTodos(
      todos.map((todo) =>
        todo.text === text ? { ...todo, completed: !todo.completed } : todo
      )
    );
  };

//eliminando todo
  const deleteTodos = (text) => {
    setTodos(todos.filter((todo) => todo.text !== text));
  };

Podemos modificar la función completeTodos para que podamos volver a marcarlo como incompleto, en caso de que nos equivoquemos o que al fin resulte que no completamos efectivamente ese ToDo jeje

    const toggleCompleteTodo = (text) => {
        const todoIndex = todos.findIndex(todo => todo.text == text);
        const newTodos = [...todos];

        newTodos[todoIndex].completed = !newTodos[todoIndex].completed

        setTodos(newTodos)
    }

También podemos simplificar la función deleteTodo con el método filter, que retorna un nuevo array:

const deleteTodo = (text) => {
        setTodos(
            todos.filter(todo => todo.text != text)
        )
    }

profe si que explicas bien.

Con lo visto en esta clase y los aported de otros compañeros le reduje a mi solucion como 8 lineas de codigo :'v

Agregue un id para cada funcion (toggle y delete), para evitar posibles errores , y use el metodo splice de Array para manejar esos casos .

Hola, yo hice el completeTodos de otra manera (incluyendo el toggle), ¿Qué tal les parece (bien o mal)?

  const completeTodos = (text) => {
    let todoFound = todos.find( todo => {
      return todo.text === text;
    });

    todoFound.completed = !todoFound.completed;
    
    //crear una nueva lista de  ToDos para que aplique el cambio
    const newTodoList = [...todos];
    setTodos(newTodoList);
  }

Si se usa:

<const todoIndex = todos.findIndex(todo => todo.text === text)>

Va a afectar la aplicacion, ya que si, pusieramos dos o mas todos con el mismo texto, lo que pasaria es que solamente podria completar uno de esos dos o mas todos, por la tanto para evitar errores seria mejor usar:

<const todoIndex = todos.findIndex(todo => todo.id === text)> 

Ya que como el id es un identificador unico de cada todo, entonces no afectaria si hay mas de un todo con el mismo texto

Completando y eliminando TODOs

Eliminar TodoS

  const deleteTodo = (key) => {
  const newTodos = todoS.filter(todo => todo.text != key);
  setTodo(newTodos);
  };

En mi caso he querido realizar un setTimeOut para que, despues de pasado el tiempo de la tarea completada, se eliminara. AQUI DEJO EL CODIGO

const [todos,setTodo]=React.useState(defaulttodos);
  const [search, setSearch] = React.useState('');

  //Para contar la cantidad de tareas realizadas
  const completedTodos = todos.filter(todos => !!todos.completed ).length;
  //Contar la cantidad de las tareas 
  const totalTodos = todos.length;

  const completeTodos = (text)=>{
  const todoIndex = todos.findIndex(todo =>todo.text === text);
  const newTodos = [...todos];
  newTodos[todoIndex] = {
    text: todos[todoIndex].text,
    completed: true,
  };
  setTodo(newTodos);
  // alert(`Has completado la tarea ${todos[todoIndex].text}`)
  setTimeout(deleteTodos,5000)
  }
const deleteTodos=(text)=>{
  const todoIndex = todos.findIndex(todo=>todo.completed===true)
  if(todoIndex===false){
    console.log('La tarea aun no esta lista')
  }else{
    const newTodos=[...todos];
    newTodos.splice(todoIndex,1)
    setTodo(newTodos)
    alert('La tarea completada sera eliminada')
  }
}```

he aqui mi codigo, he tratado de hacer la inteligencia un poco diferente a la del profe(muy poco ) pero mellvo lo bailado por completo de la funcion delete no sin agradecer una busque en google de como eliminar elementos de un array, en fin he aca mi codigo…

const completeTodo = (identificador) =>{
let index = todos.findIndex(todo => todo.text === identificador)
let newArray = [...todos]
newArray[index].completed=true
setTodos(newArray)
}

const deleteTodo = (identificador) =>{
  let deleteElement = todos.filter(todo => todo.text !== identificador)
  setTodos(deleteElement)
  
  }

le puse a mi codigo una alerta antes de eliminarlo para evitar errores este es el codigo:

le agregé una propiedad delete al array todos y dependiendo de esta propiedad se renderiza el cuadro de confirmación

const deleteTodos = (text)=>{
    const newTodos = [...todos];
    const todoIndex = todos.findIndex(todo => todo.text == text);
    newTodos[todoIndex].deleted = true;
    setTodos(newTodos) 
  }
  const confirmDelete = (text) =>{
    const todoIndex = todos.findIndex(todo => todo.text == text);
    const newTodos = [...todos];
    newTodos.splice(todoIndex, 1);
    setTodos(newTodos)
  }
  const cancelDelete = (text) =>{
    const newTodos = [...todos];
    const todoIndex = todos.findIndex(todo => todo.text == text);
    newTodos[todoIndex].deleted = false;
    setTodos(newTodos) 
  }

<TodoList>
        {todoSearched.map(todo =>(
          <TodoItem 
            key ={todo.text} 
            text = {todo.text} 
            completed = {todo.completed}
            confDelete = {todo.deleted}
            onComplete = {()=> completeTodos(todo.text)} 
            onDelete= {()=> deleteTodos(todo.text)}
            onConfirmDelete={()=> confirmDelete(todo.text)}
            onCancel = {()=>cancelDelete(todo.text)}
          />
        ))} 
      </TodoList>

// este es el componente  todoItem
function TodoItem(props){

 
 return(
  <li className="item-list">
   <span 
   className={`check ${props.completed && 'check-on'}`}
   onClick={props.onComplete}
   ></span>
   <p className={`item-p ${props.completed && 'item-p-completed'}`}>{props.text}</p>
   <span 
   className="delete"
   onClick={props.onDelete}
   ></span>
   <div className={`confirmation ${props.confDelete && 'confDelete'}`}>
    <h3 className="title">¿Estas seguro de eliminar este ToDo?</h3>
    <span 
    className="confButton"
    onClick={props.onCancel}>cancelar</span>
    <span 
    className="confButton yes"
    onClick={props.onConfirmDelete}
    >confirmar</span>
   </div>
  </li>
 )
}

En caso de querer una función más dinámica y que valide si la tarea esta completada pase a por hacer y viceversa.

const completeTodos(text) = (text) => {
	const todoIndex = todos.findIndex(todo => todo.text === text)

	let is_completed = newTodos[todoIndex].completed;

	const newTodos = [...todos];
	# Aqui viene el cambio
	newTodos[todoIndex].completed = is_completed ? false: true;
}

Me parece mas fácil eliminar los todos de esta forma:

function tareaEliminada(item){

      const tareasEliminadas = listaTareas.filter( 
        tarea => tarea.nombre !== item.nombre)
       setListaTareas(tareasEliminadas)
    
    }

Podemos utilizar nuestro operador ternario para cambiar el estado de nuestras ToDos

_si te equivocas y realizas una tarea que no has realizado puedas volver a desmarcar _

 const completeTodo = (text) => {
    const todoIndex = todos.findIndex(todo => todo.text === text)
    const newTodos = [...todos]
    newTodos[todoIndex].completed ? newTodos[todoIndex].completed = false : newTodos[todoIndex].completed = true
    setTodos(newTodos)
  }

mi lista de tareas

Yo me emociono igual que JuanDC cuando me funcionan el código jeje 😁😊

Me encanta que el profe mantenga la “lógica de negocio” alejada de la vista de cada uno de los componentes.

Encontró la manera perfecta de explicarnos y en el proceso no enseñarnos malas prácticas.

Amo este curso ❤️

Otra forma para el método de borrar los TODOs

 const deleteTodo = (text) => {
    const newTodos = todos.filter(todo => todo.text !== text)
    setTodos(newTodos)
  }

me alegra que hice el deletetodo por cuenta propia antes de que juan lo hiciera

Todos los pequeños detalles que agregué:

Mi forma mas sencilla de marcar o desmarcar una tarea fue esta:

const completeTodos = (text) => {
    const clickedTodo = todos.find( todo => todo.text === text)
    clickedTodo.completed = !clickedTodo.completed
    setTodos([
      ...todos
    ])
  }

y funciona!
Siendo sincero, estaba codeando y este código me salió sin querer y ni sabía por qué funcionaba.
Después de un rato analizando, vi que funciona porque la variable clickedTodo solo es un pointer al objeto original en memoria, así que editar una propiedad del clickedTodo cambiará el objeto original también. Entonces, sin necesidad de crear un array de todos nuevo, envié el mismo array todos pero ahora editado, listo para re-renderizar.

Yo solo le puse un condicional que si el todo ya esta en true que lo pase a false y sino que lo pase a true,
tmb en el delete use filter porque asi lo pense y cuado vi como lo hizo el profe bueno lo deje asi me parece que filter es mas corto y facil de leer

const completeTodo =(id)=>{
    const todoIndex = todos.findIndex(todo=> todo.id === id)
    const newTodos =[...todos];
    newTodos[todoIndex].complete? newTodos[todoIndex].complete=false : newTodos[todoIndex].complete=true;
    setTodos(newTodos)
  }
  const deleteTodo =(id)=>{
    let newTodos =[...todos];
    newTodos=newTodos.filter(todo =>todo.id !== id)
    setTodos(newTodos)
  }

Alternativas para los métodos de completar y borrar:

- Completado: Podemos definir el método toggleCompleted que nos permite marcar y desmarcar como completado, para esto utilice el método map, aunque se podría seguir utilizando el del profe.

const toggleCompleted = (text) => {
    setTasks(tasks.map((task) => {
      if (task.text === text) return { ...task, completed: !task.completed };
      return task;
    }))
  };

- Borrar tarea: Podemos utilizar filter para retornar a nuestro array de todos todas las tareas que NO cumplan con la que queremos borrar, y retornando nada si encontramos la que queremos borrar, para de ese modo quitarla del array de todos.

const deleteTask = (text) => {
    setTasks(tasks.filter((task) => {
      if (task.text !== text) {
        return task;
      }
      return;
    }))
  }

Es hasta este punto que voy entendiendo lo importante que es comprender los arrays en Js en el manejo de REACT

Para los que no lo han completado aquí esta el curso:

https://platzi.com/cursos/arrays/

Hola, les comparto mis funciones para completar y eliminar ToDos:

const completeToDo = (text) => {
    const updatedTodos = todos.map(todo => {
      return {
        text: todo.text,
        completed: todo.completed || todo.text === text
      }
    });
    setTodos(updatedTodos);
  }

  const deleteToDo = (text) => {
    const updatedTodos = todos.filter(todo => todo.text !== text);
    setTodos(updatedTodos);
  }

Por si a alguien le sirve, la diferencia entre == y ===, es:

== compara dos datos aunque su tipo sea diferente. Si los tipos de datos son diferentes entonces convierte uno de los valores al otro tipo y compara si son iguales para devolverte true.

=== Revisa que tanto los datos, así como su tipo, sean iguales. Si los tipos de datos son diferentes entonces devolverá false sin ninguna conversión.

Yo borre el Item con ayuda de .Filter envés de .Splice y ademas utilize .Find para no permitirle que se borre sin antes haber completado la tarea. Y todo con la ayuda de MDN!

 const deleteTodo = (text) => {
    const todoSelect = todos.find((todo) => todo.text == text);
    console.log(todoSelect.completed);
    if (!todoSelect.completed) {
      alert("Debes completarlo primero!!");
    } else {
      const deleteTodo = todos.filter((todo) => todo.text !== todoSelect.text);
      setTodos(deleteTodo);
    }
  };

La emocion de que ande todo de primera no se va nunca jaja

Por aquí les dejo un pequeño aporte para reducir un poco nuestro código…

agrege una funcion de los mas sencilla para poner el item de "todo check"despues de un click en verdadero, con dos click denuevo en falso.

import React from "react";
import './TodoItem.css';
function TodoItem(props){
    return(
        <li className="TodoItem">
            <span className= {`Icon Icon-check ${props.completed && 'Icon-check--active'}`}
            onClick={props.onComplete} onDoubleClick={props.noComplete}
            ></span>
            <p className={`TodoItem-p ${props.completed && 'TodoItem-p--complete'}`}
          
            >
            {props.text}
                </p>
            <span className="Icon Icon-delete"   onClick={props.onDelete}
            >
            <ion-icon name="trash-outline"></ion-icon>
             
                </span>
        </li>
    );
}

export { TodoItem };

codigo de app

import React from 'react';
import { TodoCounter } from './TodoCounter';
import { TodoSearch } from './TodoSearch';
import { TodoList } from './TodoList';
import { TodoItem } from './TodoItem';
import { CreateTodoButton } from './CreateTodoButton';
// import './App.css';

const defaultTodos = [
  { text: 'Cortar cebolla', completed: true },
  { text: 'Tomar el cursso de intro a React', completed: false },
  { text: 'Llorar con la llorona', completed: true },
  { text: 'LALALALAA', completed: false },
];

function App() {
  const [todos, setTodos] = React.useState(defaultTodos);
  const [searchValue, setSearchValue] = React.useState('');

  const completedTodos = todos.filter(todo => !!todo.completed).length;
  const totalTodos = todos.length;

  let searchedTodos = [];

  if (!searchValue.length >= 1) {
    searchedTodos = todos;
  } else {
    searchedTodos = todos.filter(todo => {
      const todoText = todo.text.toLowerCase();
      const searchText = searchValue.toLowerCase();
      return todoText.includes(searchText);
    });
  }
  const completeTodos = (text)=> {
    const todoIndex = todos.findIndex(todo =>todo.text === text);
    const newTodos = [...todos];
     newTodos[todoIndex].completed = true;
    setTodos(newTodos);
  
  };
  const descompleteTodos = (text)=> {
    const todoIndex = todos.findIndex(todo =>todo.text === text);
    const newTodos = [...todos];
     newTodos[todoIndex].completed = false;
    setTodos(newTodos);
  };
  const deleteTodo = (text) => {
    const todoIndex = todos.findIndex(todo => todo.text === text);
    const newTodos = [...todos];
    newTodos.splice(todoIndex, 1);
    setTodos(newTodos);
  };
  return (
    <React.Fragment>
      <TodoCounter
        total={totalTodos}
        completed={completedTodos}
      />
      <TodoSearch
        searchValue={searchValue}
        setSearchValue={setSearchValue}
      />

      <TodoList>
        {searchedTodos.map(todo => (
          <TodoItem
            key={todo.text}
            text={todo.text}
            completed={todo.completed}
            onComplete={() => completeTodos(todo.text)}
            noComplete={() => descompleteTodos(todo.text)}
            onDelete={() => deleteTodo(todo.text)}
          />
        ))}
      </TodoList>

      <CreateTodoButton />
    </React.Fragment>
  );
}

export default App;

el problema es que no se si el ondbclick es mala practica de esa manera por que pensaba hacerlo con if pero asi es mas sencillo.

He notado que al usar solo todos cuando queremos marcar una tarea completada y sin usar el array de **newTodos **para **setTodos(todos) ** el componente del Item de React no se re-renderiza. Solo cuando hago un cambio en el codigo.

const todoIndex = todos.findIndex(todo => todo.text === text);
    todos[todoIndex].completed = true;
    setTodos(todos);

Interesante. Eso me hace pensar un poco en como React renderiza los componentes, todavia no tengo la certeza pero seguro que lo veremos en el curso. 😄


completando y eliminando todos
aprendiendo a elegir y modificar cambios

¡Hola! Les comparte mi función para borrar cosas, usa filter en lugar de lo que David usó, funciona igual pero es ligeramente más cortito 😄

	const deleteTodo = (text) => {
		const newTodos = todos.filter(todo => todo.text !== text);
		setTodos(newTodos);
	};

Mi practica

App.js

import React from 'react';
import {v4 as uuidv4} from 'uuid';
import {TodoCounter, TodoSearch, TodoList, TodoItem, CreateTodoButton} from "./components";
// import logo from './logo.svg';
//import './App.css';

const defaultTodos = [
    {id: uuidv4(), text: 'Cortar cebolla', completed: false},
    {id: uuidv4(), text: 'Tomar el curso', completed: true},
    {id: uuidv4(), text: 'Ir a dormir', completed: false},
];

function App() {
    const [todos, setTodos] = React.useState(defaultTodos);
    const [searchValue, setSearchValue] = React.useState('');
    const completedTodos = todos.filter(todo => !!todo.completed).length;
    const totalTodos = todos.length;
    let searchedTodos = [];
    if (searchValue.length >= 1) {
        searchedTodos = todos.filter(todo => {
            let todoText = todo.text.toLowerCase();
            let searchText = searchValue.toLowerCase()
            return todoText.includes(searchText);
        });
    } else {
        searchedTodos = todos;
    }

    const toggleCompleteTodos = (id) => {
        const todoIndex = todos.findIndex(todo => todo.id == id);
        const newTodos = [...todos];

        (newTodos[todoIndex].completed === false)
            ? newTodos[todoIndex].completed = true
            : newTodos[todoIndex].completed = false;

        setTodos(newTodos);
    };
    const deleteTodo = (id) => {
        const todoIndex = todos.findIndex(todo => todo.id == id);
        const newTodos = [...todos];
        console.log(newTodos);
        newTodos.splice(todoIndex,1);
        console.log(newTodos);
        setTodos(newTodos);
    };

    return (
        <React.Fragment>
            <TodoCounter
                completed={completedTodos}
                total={totalTodos}
            />
            <TodoSearch
                searchValue={searchValue}
                setSearchValue={setSearchValue}
            />
            <TodoList>
                {searchedTodos.map(todo => (
                    <TodoItem
                        key={todo.id}
                        text={todo.text}
                        completed={todo.completed}
                        onToggleComplete={() => toggleCompleteTodos(todo.id)}
                        onDelete={()=>deleteTodo(todo.id)}
                    />
                ))}
            </TodoList>

            <CreateTodoButton/>
        </React.Fragment>
    );
}

export default App;

TodoItem.js

import React from 'react';
import './TodoItem.css';
import CheckBoxIcon from '@mui/icons-material/CheckBox';
import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
import CloseIcon from '@mui/icons-material/Close';

function TodoItem(props) {
    const onToggleComplete = props.onToggleComplete;
    const onDelete = props.onDelete;


    return (
        <li className="TodoItem">
            <span
                className={`Icon Icon-check ${props.completed && 'Icon-check--active'}`}
                onClick={onToggleComplete}
            >
                {
                    props.completed
                        ? <CheckBoxIcon/>
                        : <CheckBoxOutlineBlankIcon/>
                }

            </span>
            <p className={`TodoItem-p ${props.completed && 'TodoItem-p--complete'}`}>{props.text}</p>
            <span
                className="Icon Icon-delete"
                onClick={onDelete}
            ><CloseIcon/></span>
        </li>
    )
}

export {TodoItem};

esta es mi solucion para cambiar el eliminar todos

  const deleteTodo = (text) => {
    const updatedTodos = todos.filter( todo => todo.concept !== concept)
    settodos(updatedTodos)
  }

Muy buenas estas clases, con estas explicaciones, me gustó lo del pan tajado jajaja.

Otra forma de hacerlo… es pasarle el index desde .map

searchTasks.map( (task, index) => (
    <TodoItem key={task.id} task={task} 
                              changeCompleteTask={() => changeCompleteTask(index)}
                              delTask={() => delTask(index)} />
            ))

no me sorprenderia si mi profe a los 30 años es el nuevo elon musk

Para poder marcar y desmarcar un Todo (a veces hacemos missclick o nos distraemos y marcamos el que no era) solo es poner una condicional que verifiqué si tiene estado “false” lo cambie a “true” o viceversa. 😀

  const completeTodo = (text) => {
    const todoIndex = todos.findIndex(todo => todo.text === text);
    const newTodos = [...todos];

//La condicional es la siguiente:
    newTodos[todoIndex].completed === false ? newTodos[todoIndex].completed = true : newTodos[todoIndex].completed = false;    
    setTodos(newTodos);
  }

Me gusta escribir las variables globales en mayúsculas, para no confundirme e identificarlas rapidamente.

Intenté crear estados individuales por cada todo en sus atributos ‘completed’ y ‘deleted’. Pero para hacerlo debía crear un nuevo componente dentro del componente que ya tenía, dado que no dejaba crear los Hooks. El objetivo era ejecutar la actualización desde el mismo componente ‘List Item’. Observé que no podía actualizar los estados originales, supongo por lo que estos estados realmente no estaban dentro de App, sino de cada ‘Item’, y sí, se guardaban los valores de los estados (encapsulamiento? 🤨), pero no quedaban registrados en los Todos originales. Probé copiar la lista de Todos y modificar la nueva lista, para luego usar ‘set’, pero me generó un error por muchos Renders. En conclusión, no es buena idea JAJAJA 🤣

Dejo el código para que se hagan un idea del enredo! 👇

  function LoadTodosListItem (todo) {
    const [completed, setCompleted] = React.useState(todo.completed);
    const [deleted, setDeleted] = React.useState(todo.deleted);
    
    return (
      <TodoItem key={todo.text} text={todo.text} completed={completed} setCompleted={setCompleted} deleted={deleted} setDeleted={setDeleted} color={todo.color} todos={todos} setTodos={setTodos} />
    )
  }

Y…

      <TodoList>
        {searchedTodos.map(todo => LoadTodosListItem(todo))}
      </TodoList>

Que clase tan genial

no me salio T.T
pero estoy feliz

Otra forma de hacer el toggle:

const toggleTodo = (text) => {
  const todoIndex = todos.findIndex((todo) => todo.text === text);
  setTodos((prev) => {
    prev[todoIndex].completed = !prev[todoIndex].completed;
    return [...prev];
  });
};

La función setTodos del react recibe por parámetro un valor prev interno, ese valor es un estado previo de todos, por lo que se puede trabajar sobre ese mismo valor sin necesidad de crear una nueva variable.
Sí, es un poco más complejo, pero es otra forma (?)

Otra forma de hacer deleteTodo

const deleteTodo = (text) => {
  setTodos((prevTodo) =>
    prevTodo.filter((todo) => !todo.text.includes(text))
  );
};

setTodo toma prevTodo como parámetro propio que retorna el estado anterior de todos. Se pasa un arrow function y se filtra para que retorne los todo que no incluyan el texto.

Así es como me queda usando las librerías de SweetAlert2 y para ello debes ejecutar en la terminal o CMD el comando:
npm install --save sweetalert2 sweetalert2-react-content

y después para el tema oscuro de las alertas:
npm install @sweetalert2/theme-dark


Por último en los componentes de “.js” debes importarlos, en App.js importas:
import ‘@sweetalert2/theme-dark’;

En los componentes donde vallas a mostrar la alerta importas:
import Swal from “sweetalert2”;

En estos ejemplos, estas son una parte de mi código de las alertas:

const completeTodo = (text) => {
const todoIndex = todos.findIndex(todo => todo.text === text);
const newTodos = [...todos];
Swal.fire({
  title: "¿Marcar Todo?",
  background: "#000",
  showCancelButton: true,
  confirmButtonColor: "#31b904",
  cancelButtonColor: "#d30404",
  cancelButtonText: "NO",
  confirmButtonText: "SÍ",
  footer: text
}).then((result) => {
  if (!!result.value) {
    newTodos[todoIndex].completed = true;
    setTodos(newTodos);
  }
})
};


const deleteTodo = (text) => {
const todoIndex = todos.findIndex(todo => todo.text === text);
const newTodos = [...todos];
Swal.fire({
  icon: 'question',
  title: "¿Eliminar Todo?",
  background: "#000",
  confirmButtonColor: "#d30404",
  showCancelButton: true,
  cancelButtonColor: '#31b904',
  cancelButtonText: "NO",
  confirmButtonText: "SÍ",
  footer: text
}).then((result) => {
  if (!!result.value) {
    newTodos.splice(todoIndex, 1);
    setTodos(newTodos);
  }
})

};

Este curso es buenisimo!!! D:

Hasta este momento , un excelente curso de React, no puedo esperar para segui con el proximo!!!

Con este código extra la función completeTodo también habilita el desmarcar como hechos los ‘todos’

const completeTodo = (text) => {
const todoIndex = todos.findIndex(todo => todo.text === text)
const newTodos = […todos]
if(!newTodos[todoIndex].completed) {
newTodos[todoIndex].completed = true
} else {
newTodos[todoIndex].completed = false
}
setTodos(newTodos)
}

Juan contagia la felicidad cuando corre bien la aplicación jajajaja

A mi me gusta más enviar en props el objeto completo, en ocasiones te ahorra enviar múltiples propiedades. En este caso como trabajamos con todas las propiedades del todo creo que conviene mucho.
Ejemplo:

 <TodoItem key={todo.text} todo={todo}
                            completeTodo={completeTodo}
                            deleteTodo={deleteTodo}
                  />

Para completar el todo se simplifica la función:

const completeTodo = (todo) => {
    let index = todos.indexOf(todo);
    let newTodos = [...todos];
    newTodos[index].completed=true;
    setTodos(newTodos);
  }

Y en el componente trabajas con el “objeto” que te interesa:

 <span className={`Icon Icon-check ${props.todo.completed && 'Icon-check--active'}`}
                onClick={onComplete}
          >

Mi solucion

  const [completed, setCompleted] = useState(props.completed);

  const handleComplete = (event) => {
    event.target.checked ? setCompleted(true) : setCompleted(false);
  };

Tambien se puede hacer una forma mas simplificada en deleteTodo:

const deleteTodo = (text) => {
  const newTodos = todos.filter(todo => todo.text !== text);

  setTodos(newTodos);
}

De esta forma no buscamos el indice ni nada. Simplemente creamos un nuevo array con la funcion filter, con los todos que tengan un texto diferente al que le enviamos por parametro.

Esta forma de utilizar el codigo, es util cuando estamos haciendo una App de TODOs con POO

    todos[todoIndex] = {
      text: todos[todoIndex].text,
      completed:true,
    };

Recien termine el curso de POO con Anncode y mi mente visualiza todo en modo “Objetos”

Que bueno por el profesor que se tomo la molestia de explicar muy detalladamente la funcion completeTodos.

Excelente el filtro

este es mi codigo! en mi caso tuve que buscarlo con un for porque las otras funciones me daban errores o no era lo esperado.

import './TodoItem.scss';
import checked from './assets/checked.svg';
import deleted from './assets/delete.svg';
function TodoItem(props){
    const checkTodo =()=>{
        if(props.completed === false){
            // busca el todo con el text correspondiente
            for (let i = 0; i < props.todos.length; i++) {
                let todo = props.todos[i];
                if(todo.text === props.text){
                    todo.completed = true;
                }
            }
            // crea un nuevo arraya con el todo seleccionado con completed como true
            let newTodos = props.todos;
            // setea mediante una props el nuevo array
            props.setTodos([...newTodos])
        }else{
        }
    }
    const deleteTodo =()=>{
        let newTodos = props.todos.filter(todo => todo.text !== props.text);
        props.setTodos(newTodos)
    }
    return(
        <li className={`TodoItem ${props.completed && 'TodoItem-completed'}`} >
            <p>{props.text}</p>
            <div className='TodoItem__buttons'>
                <button onClick={checkTodo}>
                    <img src={checked} alt="check task" title='check task'/>
                </button>
                <button onClick={deleteTodo}>
                    <img src={deleted} alt="delete task" title='delete task'/>
                </button>
            </div>
            
        </li>
    );
}

export { TodoItem };

Para este punto, podría crear un proyecto sencillo para la cual, podría aplicar todos estos conocimientos creando un proyecto desde cero.
Ya les contare mis avances.

Creo que sería bueno, dentro de la función completeTodo, colocar un condicional para que en caso de que newTodos[todoIndex].completed sea false, se cambie a true como ya hizo el profesor, pero que si es true, cambie a false y así el símbolo check vuelva al color negro y el “todo” ya no aparezca tachado, esto removiendo las clases que le hicieron tomar dichos estilos.

onComplete no sería un método? porque lo llama atributo?

¡Lo logré, conseguí crear mi botón para eliminar TODOS!

https://github.com/luiznaiper/react-platzi-proyect-1.git

Lo que hice fue filtrar los TODOS que tuvieran la propiedad completada como falso y actualicé el estado:

const deleteCompletedTodos = ()=> {
    const filterTodos = todos.filter(todo => todo.completed === false)
    console.log(filterTodos)
    setTodos(filterTodos)
  }
      <ClearCompletedButton
        total = { totalTodos }
        completed={ completedTodos }
        deleteCompletedTodos={ ()=> deleteCompletedTodos()}
      />

Me costó muchísimo pero se logró

No es tan necesario y útil usar el text, y buscar con filter en cada operación, con el index del .map() se hubiera podido editar eliminar el todo directamente con su index.

Pueda que haya sido por alguna razón que desconozco pero también se podía sacar en “index” a la hora de iterar con el método “map”.

<TodoList>
        {searchedTodos.map((todo, index) => (
          <TodoItem
            key={todo.id} 
            text={todo.text} 
            completed={todo.completed} 
            completar={() => completeTask(index)}
            remover={() => removeTask(index)} />
        ))}
      </TodoList>

Metodos:

 const completeTask = (index) => {
    const newTodos = [...todos];
    newTodos[index].completed = true;
    setTodos(newTodos);
  }

  const removeTask = (index) => {
    const newTodos = [...todos];
    newTodos.splice(index,1);
    setTodos(newTodos);
  }
const completeTodo = (text) => {
    const todoIndex = todos.findIndex(todo => todo.text === text);
    const newTodos = [...todos];
    newTodos[todoIndex].completed = true;
    updateTodo(newTodos);
  };

  const deleteTodo = (text) => {
    const todoIndex = todos.findIndex(todo => todo.text === text);
    const newTodos = [...todos];
    newTodos.splice(todoIndex, 1);
    updateTodo(newTodos);
  };

Yo hice diferente en borrado (usando filter), les dejo mi función espero les sirva.

const deleteTodo = ( text ) => {
    const newTodos = todos.filter( todo => todo.text !== text );
    setTodos(newTodos);
  };

Así los eliminé todos y con menos líneas de código, no se si será correcto

const deleteTodo = (text) => {
    const todoFilter = todos.filter(todo => todo.text !== text)
    setTodos(todoFilter)
  }

Saludos Platzinautas.
Lo que hice fue, las variables completeTodos y totalTodos los lleve abajo, despues de la funcion if/else, y cambie los contadores de todos por searchedTodos

Eureka… funciona… super, muchas gracias…

Siento cosquillitas…