¿Cómo podemos comunicar estados entre componentes en React?
Dominar la comunicación de estados entre componentes es crucial para cualquier desarrollador de React. Aprender a interactuar los componentes mamá con sus componentes hijos te permitirá crear aplicaciones más dinámicas y robustas. En la clase anterior, se trabajó en cómo transmitir estados y en la creación de estados derivados que permiten filtrar o calcular información relevante para otros componentes en la aplicación.
¿Cómo implementamos un buscador de 'todos' eficiente?
Para que los usuarios puedan buscar tareas ('todos') dentro de nuestra lista, necesitamos crear un estado derivado que denominaremos SearchedTodos. Este estado permitirá filtrar las tareas según el texto ingresado por el usuario en el buscador.
Código para crear el estado derivado
Usaremos el método filter en los arrays para seleccionar solo los todos que contengan el valor del estado searchValue. Así se elabora el nuevo estado derivado:
Normalización a minúsculas: Esto nos asegura de que el filtrado no sea sensible a mayúsculas y minúsculas.
Renderizar el estado derivado
En lugar de renderizar directamente el array de todos por defecto, ahora debemos asegurar que la lista muestre solo los resultados filtrados que coincidan con el valor de búsqueda del usuario:
return(<TodoList todos={searchedTodos}/>);
¿Qué errores comunes podemos encontrar y cómo los solucionamos?
Un error típico es omitir el return necesario dentro del filtro, sin el cual el nuevo array de searchedTodos no se generaría correctamente. Este detalle resaltado en la clase muestra la importancia de incluir return para devolver el valor booleano que dictamina si un todo específico permanecerá en el nuevo array derivado.
Además, para mejorar la legibilidad del código, se recomienda descomponer las funciones largas en variables auxiliares. Por ejemplo:
Esta práctica no solo mejora la claridad, sino también la mantenibilidad del código.
¿Cómo se mejora la interacción del usuario en la aplicación?
Modificar la lista de 'todos' para que se ajuste dinámicamente con las búsquedas del usuario logra que la aplicación parezca más viva e interactiva. El próximo paso incluirá permitir completar o eliminar tareas, utilizando el actualizador del estado setTodos.
Se invita a los estudiantes a intentar implementar esta funcionalidad por sí mismos. El reto es lograr que, al interactuar con un componente todoItem, se afecte el estado de todos los todos, propiciando cambios en toda la aplicación. Esto incluye:
Emparejar el estado: Vincular los botones de completar o eliminar con los elementos específicos a los que corresponden.
Actualizar el estado global: Usar setTodos para que refleje los cambios en la lista y en otros componentes como todoCounter y todoSearch.
¡No importa si no logras terminarlo por completo! Lo importante es el aprendizaje adquirido al intentar resolverlo. Comparte tus hallazgos y experimentos en los comentarios.
Sigue adelante con tu aprendizaje y persiste en tu curiosidad técnica. La próxima clase te mostrará cómo realizar cada paso. ¡Nos vemos allí!
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.
Esto quiere decir que para accesibilidad es mejor usar ToLocaleLowerCase, supongo.
👉🏻 Encontré otro 🚧error🚧 para el filtro.
.
Si buscas una palabra con tílde como "Canció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 "cancion" sin tilde y te aparecerá.
const searchedTodos =Todos.filter((Todo)=>{// función texto sin tildesconstnoTildes=(text)=>{return text.normalize('NFD').replace(/[\u0300-\u036f]/g,'');};// Normalizando texto sin tildes y a Lower CaseconstTodoTextLC=noTildes(todo.text.toLowerCase());const searchTextLC =noTildes(searchValue.toLowerCase());//renderizar con filtroreturnTodoTextLC.includes(searchTextLC);});
muy buen aporte, no lo habia notado 🤓
Excelente aporte, muchas gracias!!!
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?
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(""))//Trueconsole.log(vacio.includes(""))//Trueconsole.log(nombre.includes(""))//Trueconsole.log(nombreCompleto.includes(""))//Trueconsole.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.
Gracias por la explicación.
gracias x la aclaración sergio, se me dificultó un principio
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.
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:
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).
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.
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 “reiniciar” 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}</>
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?
y aún no he escrito nada en el input, se renderizan todas las todos?
Porque el método .includes devuelve true para cualquier texto que tratemos de filtrar por un string vacío. :D
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
Definitivamente quiero agregarle categorías a TodoMachine. Vamos a ver si lo logro en algún futuro curso (y que valga la pena).
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.
Ambas formas son validas, en el primer caso el return esta implícito
con el return implícito no permite agregar mas condiciones
Me encanta que el maestro aparezca en medio del código
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 iconsimport{AiOutlineCheck,AiOutlineDelete}from'react-icons/ai';import"./TodoItem.css"functionTodoItem(props){functiongetHighlightedText(text, highlight){// Split on highlight term and include term into parts, ignore caseconst parts = text.split(newRegExp(`(${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.
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
Esta son las imágenes de como quedan
normal, pendiente todo, y dejando solo 1
La ventaja de hacerlo con un id en vez de por el texto es que si tenemos 2 textos iguales los borra a los dos, o dependiendo el metodo puede que borre solo uno, pero es mas facil te ahorra problemas y tienes mas control con el id
Con expresiones regulartes RegExp se puede solucionar el problema usando la etiqueta 'i' que ignora las mayusculas y las min[unsculas
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
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 'completed'. Lo pensé así debido a que si lo hacemos por el nombre se podrían eliminar todos los ToDo's con el mismo nombre, lo cual suena razonable pero quizá sea mejor darle la libertad al usuario de tener ToDo's iguales.