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

Buscando TODOs

9/34
Recursos

Aportes 30

Preguntas 2

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

La diferencia entre los m茅todos toLowerCase() y toLocaleLowerCase() en JavaScript est谩 relacionada con la forma en que se procesan los caracteres en may煤sculas y acentuados en diferentes idiomas.

toLowerCase() convierte una cadena de texto en min煤sculas, utilizando las reglas de conversi贸n que se aplican a los caracteres ASCII (caracteres en ingl茅s y otros idiomas europeos que no tienen acentos)

Por otro lado, toLocaleLowerCase() tambi茅n convierte una cadena de texto en min煤sculas, pero utiliza las reglas de conversi贸n espec铆ficas del idioma y la ubicaci贸n (localizaci贸n) en la que se est谩 ejecutando el c贸digo. Esto significa que, en funci贸n de la localizaci贸n, algunos caracteres con acentos o diacr铆ticos (como la letra 鈥溍♀ en espa帽ol) pueden ser convertidos a su equivalente en min煤sculas, mientras que otros caracteres pueden permanecer sin cambios.

馃憠馃徎 Encontr茅 otro 馃毀error馃毀 para el filtro.
.
Si buscas una palabra con t铆lde como 鈥淐anci贸n鈥 no la encontrar谩s.

Para eso implement茅 una linea de c贸digo que hace que las tildes las quita de las vocales, as铆 podr谩s buscar 鈥渃ancion鈥 sin tilde y te aparecer谩.

  const searchedTodos = Todos.filter(
    (Todo) => {
      
      // funci贸n texto sin tildes
      const noTildes = (text) => {
        return text.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
      };

      // Normalizando texto sin tildes y a Lower Case
      const TodoTextLC = noTildes(todo.text.toLowerCase());
      const searchTextLC = noTildes(searchValue.toLowerCase());

      //renderizar con filtro
      return TodoTextLC.includes(searchTextLC);
    }
  );

Para los que de pronto tuvieron tambi茅n dificultad en entender porque si searchValue es vac铆o, porque devuelve todos los valores del array cuando se filtra?

const searchedTodo= todos.filter((todo) => todo.text.toLowerCase().includes(searchValue.toLowerCase()));

Lo primero estamos aplicando el m茅todo includes de strings, es decir:

todo.text.toLowerCase() // string
todo.text.toLowerCase().includes(searchValue.toLowerCase())) // booleano

Si aplicamos un includes cuyo valor es vacio el va devolver un TRUE, por ejemplo:

const letra = "S"
const vacio = ""
const nombre = "Sergio"
const nombreCompleto = "Sergio Aguilar"
const numero = 鈥3鈥

console.log(letra.includes("")) //True
console.log(vacio.includes("")) //True
console.log(nombre.includes("")) //True
console.log(nombreCompleto.includes("")) //True
console.log(numero.includes("")) //True

Como resultado cada elemento(todo) recorrido va ser True y por ende el filter aplicado va devolver cada elemento del array.

Clase 9 - Buscando TODO鈥檚

Para esta clase utilizaremos estados derivados, por lo tanto definimos una constante:

const searchedTodos = todos.filter(
  (todo) => {
    return todo.text.include(searchValue)
  }
)

Aqui lo que hacemos es buscar dentro de los textos de los todos el valor de searchValue creando un nuevo array con las coincidencias.

Luego debemos cambiar el comportamiento de del componente TodoList para que trabaje con el nuevo array, cambiando el defaultTodos con searchedTodos:

<TodoList>
  {searchedTodos.map(todo => (
    <TodoItem
      key={todo.text}
      text={todo.text}
      completed={todo.completed}
    />
  ))}
</TodoList>

Esto ya funciona, pero tiene un detalle no reconoce mayusculas o minusculas de acuerdo como busquemos, asi que lo que haremos es optimizar la busqueda para que del lado de la logica comvierta todo el texto en minusculas:

const searchedTodos = todos.filter(
  (todo) => {
    return todo.text.toLowerCase().include(searchValue.toLowerCase())
  }
)

Ahora optimizamos el codigo:

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

En un ambito profesional (dependiendo cada caso de uso), para un campo de buscar, me gusta normalizar ambas strings, ignorando mayusuclas, ignorando acentos, quitando espacios, en cualquier posicion de la string.

Mi m茅todo:

const normalizeString = (string) => {
    string = string || "";
    string = string.toLowerCase();
    // remover acentos
    string = string.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
    string = string.trim();
    return string;
  };

y lo uso asi:

const filteredTodos = todos.filter((todo) => {
    let { text: normalizedTodo } = todo;
    normalizedTodo = normalizeString(normalizedTodo);
    let normalizedSearch = normalizeString(searchValue);

    return normalizedTodo.includes(normalizedSearch);
  });

Espero le sirva a alguien 馃槂

saludos.
yo decid铆 usar match con indicador/bandera i, para que se ignoren las diferencias entre may煤sculas y min煤sculas.

  const searchedTodos = Lista.filter(dato => {
    const Reg = new RegExp(Buscar, 'i');
    if (dato.text.match(Reg) != null)
    {
      return dato;
    }
  });

Hola! tengo una pregunta, porque el filter me devuelve todos los elementos del array si no hay ningun valor en searchValue? no deberia quedar vacio?

Mi soluci贸n al reto: Los m茅todos de los arrays (Map, Find, Filter, etc) tienen un par谩metro llamado index, ese index permite conocer la posici贸n del elemento que se accede en el array, teniendo en cuenta lo anterior creamos 3 hooks de estado y enviamos el set del componente padre al hijo (App.js > TodoItem.js).

  const [todoIndex, setTodoIndex] = useState(null);
  const [removeTodo, setRemoveTodo] = useState(null);
  const [changeTodoCompleted, setChangeTodoCompleted] = useState(null);
<>
          {searchedTodos.map((todo, index) => (
            <TodoItem
              key={index}
              index={index}
              taskText={todo.text}
              completed={todo.completed}
              setRemoveTodo={setRemoveTodo}
              setChangeTodoCompleted={setChangeTodoCompleted}
              setTodoIndex={setTodoIndex}
            />
          ))}
</>

En el componente TodoItem al momento de hacer click en uno de los iconos, asignamos el valor del index a nuestro setTodoIndex y setRemoveTodo (贸sea almacenamos el n煤mero del 铆ndice) y con setChangeTodoCompleted lo que hacemos es guardar un valor boolean que permita cambiar la tarea de completado a no completado.

  return (
    <li className="todoItemList">
      <span
        onClick={() => {
          setTodoIndex(index);
          if (!completed) {
            setChangeTodoCompleted(true);
          } else setChangeTodoCompleted(false);
        }}
      >
        <CheckIcon completed={completed} />
      </span>
      <p
        className={`TodoItem-p ${completed && "TodoItem-p--complete"}`}
      >{`${taskText}`}</p>
      <span className="removeItem" onClick={() => setRemoveTodo(index)}>
        <RemoveIcon />
      </span>
    </li>
  );

En App.js hacemos un find a nuestros todos y validamos que el index coincida con el todoIndex para luego cambiar el estado del todo.completed en true o false, as铆 mismo, asignamos null al valor de la variable con el fin de 鈥渞einiciar鈥 el estado y evitar cambiar un item err贸neo. Para el eliminar sucede algo similar solo que en vez de hacer un find hacemos un splice entre la posici贸n del index y la cantidad de elementos a eliminar.

<>
          {
            // Change the todo state
            todoIndex !== null
              ? todos.find((todo, index) => {
                  if (index === todoIndex) {
                    setTodoIndex(null);
                    todo.completed = changeTodoCompleted;
                    return todo;
                  }
                })
              : null
          }
          {
            // Delete todo
            removeTodo !== null
              ? todos.splice(removeTodo, 1) && setRemoveTodo(null)
              : null
          }
</>

Para mi app, yo no utilice un buscador, pero si agrege categorias que filtran los todos. Tambien agrege la opcion para eliminar los todos que solo has completado.
Un Gif de la app se puede ver en link de abajo. Intente ponerlo como visible desde el aporte, pero por alguna razon no lo logre. El link es de drive.
Muestra de la app

Hola, buenas 馃憢. Les comparto mi soluci贸n al reto de la clase anterior de tratar de hacer coincidir las b煤squedas.

Mi soluci贸n es muy b谩sica y simplemente es si la b煤squeda es exactamente igual a alg煤n TODO. Pero bueno, es mi soluci贸n xd.

const TODOs = todos.map(todos => todos.text);

  const resultado = TODOs.find(elemento => elemento == searchValue);

  console.log(resultado);

Ambas formas son validas, en el primer caso el return esta impl铆cito

Con expresiones regulartes RegExp se puede solucionar el problema usando la etiqueta 鈥榠鈥 que ignora las mayusculas y las min[unsculas

const searchedTodo = todos.filter((todo) =>
    RegExp(`.*${searchValue}.*`, "i").test(todo.text)
  );

para lograr el reto de mostrar lo que el usuario busca:
primero realice un estado derivado en donde utilice el estado searchValue


const filter=todos.filter(
    (todo)=> todo.text.toLocaleLowerCase().includes(searchValue.toLocaleLowerCase())

posteriormente simplemente utilice el operador ternario para mostrar toda la lista si searchvalue es 鈥溾 y si contiene algo use filter.map en ves de defaultodos.map

<TodoList>
 {searchValue === '' ? (

   todos.map((todo) => (
     <TodoItem key={todo.text} text={todo.text} completed={todo.completed} />
   ))
 ) : (

   filter.map((todo) => (
     <TodoItem key={todo.text} text={todo.text} completed={todo.completed} />
   ))
 )}
</TodoList>

Yo hice un ligero cambio a la app, decidi que las coincidencias entre el buscador y los todos se remalcaran con un font-weight: bold.

Esto lo logre agregando el siguiente codigo a todoItem:

// ignore this line i'm just importing icons
import { AiOutlineCheck, AiOutlineDelete } from 'react-icons/ai';
import "./TodoItem.css"

function TodoItem(props){
  function getHighlightedText(text, highlight){
    // Split on highlight term and include term into parts, ignore case
    const parts = text.split(new RegExp(`(${highlight})`, 'gi'));
    return <span> { parts.map((part, i) => 
        <span key={i} style={part.toLowerCase() === highlight.toLowerCase() ? { fontWeight: 'bold' } : {} }>
            { part }
        </span>)
    } </span>;
  }
    return(
      <li className={`TodoItem ${props.completed && "active"}`} >
        <span className="">
          <AiOutlineCheck className='check'></AiOutlineCheck>
        </span>
        <p>{getHighlightedText(props.text, props.searchValue)}</p>
        <span><AiOutlineDelete className="delete"></AiOutlineDelete></span>
      </li>
    );
  }

export {TodoItem}; 

Para que el codigo funcione necesitamos agregar un prop con el estado searchValue a todoItem, basicamente el codigo envuelve las coincidencias en una etiqueta span que tiene el font-weight:bold en sus estilos.

Cada vez que juan dice pasito a pasito inevitablemente digo suave suavecito 馃槀馃槀馃槀

Les comparto mi soluci贸n al reto. No la mires sin antes intentarlo!
Como podemos ver, lo que hice fue mandarle al ToDoItem el index del ToDo en cuesti贸n para saber cual debemos eliminar y para saber a cual cambiarle el atributo 鈥榗ompleted鈥. Lo pens茅 as铆 debido a que si lo hacemos por el nombre se podr铆an eliminar todos los ToDo鈥檚 con el mismo nombre, lo cual suena razonable pero quiz谩 sea mejor darle la libertad al usuario de tener ToDo鈥檚 iguales.


App.js:


ToDoItem.js:


Cualquier oportunidad de mejora bienvenida 馃榾

Uno de los retos completados

// Funcion para marcar los jutsus completados
  const completeTodo = (id) => {
    const todoIndex = todos.findIndex((todo) => todo.id === id);
    const newTodos = [...todos];
    newTodos[todoIndex].completed = true;
    saveTodos(newTodos);
  };

  const  getTodo = (id) => {
    const todoIndex = todos.findIndex((todo) => todo.id === id);
    return todos[todoIndex]

  }

Me parece genial esto de los retos, tenemos que conseguir la manera, probablemente lo hacemos funcionar y luego comparamos a ver si hab铆a (de seguro) mejores maneras de hacerlo.

Lo abord茅 con estas dos funciones. Pasando tanto los todos como la funci贸n setTodos a cada TodoItem.

Vamos a ver si as铆 era 馃憖

Hola a todos, este es mi aporte. Para resolver el requerimiento cre茅 dos funciones que reciben como par谩metro el elemento a eliminar o marcar como completada respectivamente, esto en el archivo App.js, y pas茅 estas funciones como props del TodoItem, en el TodoItem, agregu茅 el onClick y en el cuerpo de la funci贸n llam茅 a las funciones en las props y les pas茅 el texto del item, que de igual manera paso en las props.

Archivo App.js

const itemDeleteHandler = (value) => {
       const result = todos.filter((item) => {
            return item.text !== value;
       });

       setTodos(result);
  }

  const itemDoneHandler = (value) => {
       const result = todos.map((item)=>{
            return item.text === value ? {text : item.text, completed: true} : item;
       })

       setTodos(result);
  } 
 <TodoList>
        {searchedTodos.map(todo => {
          return <TodoItem 
                  key={todo.text} 
                  text={todo.text}
                  completed={todo.completed}
                  ondelete={itemDeleteHandler}
                  oncomplete={itemDoneHandler}/>
        })}
      </TodoList>

Archivo TodoItem.js

function TodoItem(props){
    return (
          <li>
               <div className="item-container">
                <span onClick={() => {
                        props.oncomplete(props.text);
                }
                }>
                    <BsCheck2 color={props.completed ? "#198754" : "gray"} size={24} strokeWidth={2}/>
                </span>
                <p className={props.completed ? `text-completed` : ``}>{props.text}</p>
                <span onClick={() => { 
                  props.ondelete(props.text);
                }}>
                  <BsFillTrashFill color="#dc3545" size={20} id="btn-delete"/>
                </span>
              </div>
          </li>

    );
 }

Espero se entienda mi explicaci贸n. Este es mi respositorio de github:

Pd: Solo puse el c贸digo que cambie para que no sea dificil de leer.

Para solucionar el reto propuesto al final de la clase para hacer check, uncheck y delete a los TO DO鈥檚 hice para cada boton una funcion que se active en el metodo onClick.

Desde onClick le paso una variable para identificar el boton.

<span className="icons close" onClick={(event)=>{
          const message = event.target.parentElement.childNodes[2].textContent
          console.log('eliminar');
          deleteTodo(message)
        }}>
</span>

Haciendolo de esta manera puedo saber cual texto de cual to do le pertenece a cual boton segun fue clicado. El numero 2 en childNodes es porque en la estructura de mi contenedor padre el elemento [2] es el parrafo que contiene el texto de cada todo.

Luego la funcion vendria siendo la siguiente

  function checkTodo(checkThis){
    const updatedTodos=[]
    props.todos.forEach((todo)=>{
      if(todo.text===checkThis){
        todo.completed=true
        updatedTodos.push(todo)
      }else{
        updatedTodos.push(todo)
      }
    })
    props.setTodos(updatedTodos)
  }

Alli creo un array vacio, y luego evaluo el estado de todos que pase por props, y evaluo para cada todo si su propiedad text es igual a checkThis (que es el mensaje asociado al boton clicado), si es igual quiero que me le cambie la propiedad completed a true y me lo meta al nuevo array y los todo que no cumplan la condicion tambien quiero que me los meta al nuevo array.

Finalmente actualizo el estado con el nuevo Array.

Tengo un boton de uncheck asi que seria exactamente igual pero cambiando la propiedad completed a false y para el boton de delete en el nuevo array metes todos los todos exceptuando el que cumpla la condicion, que seria la misma condicion en la funcion que les mostre

Trat茅 de hacer el reto por mi cuenta al finalizar el video anterior y casi lo hago igual que en este video. Lo 煤nico que me fallo fue la parte de pasar los textos a minuscula, porque no sabia que iba con () al final.
Reto Solucionado modificaciones: Los defaults todos van a tener un id 煤nico Este id al igual que todos y setTodos lo pasamos como parametro para la funci贸n , en este caso son 2 delete y change, en el caso de delete filtramos el array todos con todos los que NO coincidan con el id pasado y ese valor se lo pasamos a setTodos en el caso del update lo que hacemos es una transformacion usamos un map que retornara cada id hasta el caso en donde el id coincida con el pasado en el parametro y con este valor le asignamos su inverso, ya luego con la transformaci贸n se la pasamos a setTodos ![](https://static.platzi.com/media/user_upload/image-ef14c797-3a1f-4510-b013-2390dedb9bb2.jpg) ![](https://static.platzi.com/media/user_upload/image-6ff0365d-b5bb-40bb-a1ae-88c2bff667e0.jpg)![](https://static.platzi.com/media/user_upload/image-994345f9-5ec1-4f9c-b171-2ed45553832c.jpg) ![](https://static.platzi.com/media/user_upload/image-82482f46-a80d-4335-8d44-f2090c00bce3.jpg)

Hello, comparto mi solucion:
Dentro del componente, TodoList, he creado un metodo llamado 鈥渄eleteItem鈥 el cual recibe 鈥渢ext鈥 que vendria siendo el texto del item a eliminar. Hago unfilter para agregar a un nuevo array los elementos que no coincidan con tal text, y luego actualizo el estado del componente:

function deleteItem(text){

     const estadoDerivado = todos.filter(todo=>todo.text !== text);
      console.log('Deleting: ' + text);
     setTodos(estadoDerivado);
         
   }

En la lecci贸n anterior, exploramos c贸mo transmitir datos entre componentes, espec铆ficamente entre componentes padres e hijos. Adem谩s, aprendimos a crear estados derivados, los cuales, a partir de un estado inicial, nos permiten realizar c谩lculos o filtrados para enviar informaci贸n relevante a otros componentes de la aplicaci贸n.

Introducci贸n a la B煤squeda de TODOs

En esta clase, abordamos un nuevo desaf铆o: implementar un sistema de b煤squeda en nuestra aplicaci贸n de lista de todos. El objetivo es permitir a los usuarios encontrar r谩pidamente los elementos de la lista que coincidan con su consulta.

Implementaci贸n de la B煤squeda de TODOs

Para lograr esto, introdujimos un nuevo estado derivado llamado searchTodos. Este estado se crea mediante el m茅todo filter de los Arrays y se basa en el estado searchValue. Cada elemento en la lista original (todos) se eval煤a para determinar si su texto incluye el valor de b煤squeda, independientemente de las may煤sculas o min煤sculas.

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

Consideraciones sobre May煤sculas y Min煤sculas

Abordamos un problema com煤n al realizar b煤squedas: la distinci贸n entre may煤sculas y min煤sculas. Para superar esta limitaci贸n, convertimos tanto el texto de cada todo como el valor de b煤squeda a min煤sculas durante la comparaci贸n. Esto garantiza que la b煤squeda sea insensible a may煤sculas y min煤sculas, proporcionando una experiencia m谩s flexible y amigable para el usuario.

const todoText = todo.text.toLowerCase();
const searchText = searchValue.toLowerCase();

Optimizaci贸n del C贸digo

Para mejorar la legibilidad y mantener un c贸digo organizado, introdujimos variables intermedias (todoText y searchText) que representan el texto convertido a min煤sculas. Esta pr谩ctica facilita la comprensi贸n del c贸digo y su mantenimiento a largo plazo.

const todoText = todo.text.toLowerCase();
const searchText = searchValue.toLowerCase();
return todoText.includes(searchText);

Desaf铆o: Completar y Eliminar TODOs

Como desaf铆o adicional, se plantea la tarea de implementar la funcionalidad de completar y eliminar todos. Cada todo debe tener la capacidad de ser completado o eliminado de manera independiente. Se alienta a los estudiantes a experimentar con el uso del actualizador de estado setTodos para lograr esta funcionalidad.

Pr贸ximos Pasos

  • Experimenta con la implementaci贸n de la funcionalidad de completar y eliminar todos.
  • Explora c贸mo transmitir el actualizador de estado setTodos a los componentes hijos para permitir interacciones espec铆ficas de cada todo.
  • Comparte tus experiencias y resultados en los comentarios para fomentar el aprendizaje colaborativo.

En la pr贸xima clase, guiaremos paso a paso la soluci贸n a este desaf铆o y exploraremos c贸mo completar y eliminar todos de manera efectiva. 隆Nos vemos all铆!

yo lo logre de esta manera: ```js <TodoList> { defaultTodos.filter(todo => todo.text.toLowerCase().includes(searchValue.toLowerCase())).map(todo => ( <TodoItem key={todo.text} text={todo.text} completed={todo.completed} onComplete={() => alert(todo.text)} onDelete={() => alert('delete')} /> )) } </TodoList> ```
solo yo estoy teniendo problemas con la preproducci贸n de las clases?? se queda colgado el video cada 10 segundos!!!

No voy a crear controversia, pero llevo ya 3 a帽os trabajando con VUE profesionalmente y realmente en developer experience esta en otro nivel de React, un poco m谩s legible el c贸digo. Sin embargo puedo apreciar porque React sigue tan posicionado. Personalmente quiero ver que onda con React y aprenderlo, porque no, pero hasta el momento no he visto una feature que me haga decir como, juemadre me cambio.

me hubiera gustado que no todo sea con un objeto defaultTodos y arrays sino interactuar con un api REST de verdad

Trat茅 de aplicar el ereto final sobre borrar toDo pero no s茅 porque solo se queda el elemento que borr茅 y en consola claramente se ve que solo quedan los otros toDos 馃槢

Sorry profe le fall茅 馃槮

Ala鈥 algo en mi mente鈥 :S
genial!!!