No tienes acceso a esta clase

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

No se trata de lo que quieres comprar, sino de quién quieres ser. Aprovecha el precio especial.

Antes: $249

Currency
$209

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Suscríbete

Termina en:

14 Días
10 Hrs
52 Min
3 Seg

Reto: página de búsquedas con navegación

26/30
Recursos

Aportes 18

Preguntas 1

Ordenar por:

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

Este reto es muy fácil con useSearchParams!! Qué lástima que no lo descubrí antes

import React from 'react'
import { useSearchParams } from 'react-router-dom'

export function TodoSearch({ setSearchValue, loading }) {
    const [searchParams, setSearchParams] = useSearchParams()
    const paramsValue = searchParams.get('search')

    const onSearchValueChange = ({ target: { value } }) => {
        setSearchValue(value)
        setSearchParams({ search: value })
    }

    return (
        <input
            className={`TodoSearch ${loading && 'TodoSearch--loading'}`}
            onChange={onSearchValueChange}
            value={paramsValue ?? ''}
            placeholder="Search a To-Do"
        />
    )
}

Para resolver el reto se puede ulitlizar el hook useSearchParams



En el HomePage.jsx agregamos:

 const [params, setParams] = useSearchParams();

Y se lo enviamos a TodoSearch:

<TodoSearch
          searchValue={searchValue}
          setSearchValue={setSearchValue}
          params={params}
          setParams={setParams}
        />

Ya en TodoSearch.jsx lo manejamos de la siguiente manera:

import React, { useEffect } from "react";

function TodoSearch({ searchValue, setSearchValue, params, setParams }) {
  const onSearchValueChange = (event) => {
    setSearchValue(event.target.value);

    let params = {
      search: event.target.value,
    };
    setParams(params);
  };

  useEffect(() => {
    const search = params.get("search") ?? "";
    setSearchValue(search);
  }, [params]);

  return (
    <input
      className="todo-search"
      placeholder="Search..."
      type="text"
      value={searchValue}
      onChange={onSearchValueChange}
    />
  );
}

export { TodoSearch };

De esa manera logramos el siguiente resultado

Uff, cuando te enfrentas a retos como este te es cuando realmente comprendes lo que creías entender de la clase.
Logré de resolverlo de la siguiente manera:

  • Dentro de App.js modifiqué la propiedad path para que aceptara el valor searchValue, también dejé el valor anterior para que la Route de HomePage siguiera funcionando aunque no se enviara el valor searchValue:
  • En TodoSearch.js utilicé useParams para cachar el valor searchValue de la url y se lo envie como value al input

    -Dentro de un useEffect() evalúo si params.searchValue existe, sí es así, actualizo el searchValue que recibimos desde props.
  • En la función onSearchValueChange agregue un navigate hacia el valor del input:

    No sé si es la mejor solución, pero funciona 😃

Mi solucion consiste en:

  1. Crear un ruta hija dinamica.
<Route path='/' element={<HomePage/>}>
    <Route path=':slug' element={<HomePage/>}/>
</Route>

Quedando asi App.js:

function App() {
  
  return (
    <HashRouter>
      <Routes>
        <Route path='/' element={<HomePage/>}>
          <Route path=':slug' element={<HomePage/>}/>
        </Route>
        <Route path='/new' element={<NewTodoPage/>}/>
        <Route path='/edit/:id' element={<EditTodoPage/>}/>
        <Route path='*' element={<p>No Found!</p>}/>
      </Routes>
    </HashRouter>
  );
}

export { App };
  1. Con el ayuda de useNavigate, creo la ruta con cada ingreso de teclado y navego a ella.
const onSearchValueChange = (event) => {
    setSearchValue(event.target.value);
    navigate('?search=' + event.target.value);
};
  1. Sí el usuario ingresa una url directa con su busqueda, me ayudo de una valorInput global y valido si hay una busqueda con ayuda de useLocation, espero a la carga del primer render y en 100ms realizo la busqueda.
let textSearchValue = searchValue;

if(location.search && !loading) {
    textSearchValue = location.search.replace('?search=', '');
    setTimeout(()=>setSearchValue(textSearchValue),50);
}

Quedando finalmente asi el archivo index.js de TodoSearch como sigue:

function TodoSearch({ searchValue, setSearchValue, loading }) {
  
  const navigate = useNavigate();
  const location = useLocation();
  let textSearchValue = searchValue;

  const onSearchValueChange = (event) => {
    setSearchValue(event.target.value);
    navigate('?search=' + event.target.value);
  };

  if(location.search && !loading) {
    textSearchValue = location.search.replace('?search=', '');
    setTimeout(()=>setSearchValue(textSearchValue),50);
  }

  return (
    <input
      className="TodoSearch"
      placeholder="Cebolla"
      value={textSearchValue}
      onChange={onSearchValueChange}
      disabled={loading}
    />
  );
}

export { TodoSearch };

A caray!!

yo utilice el hook useSearchParams para poder modificar los query params en la url, y también useEffect con un array vacio en el segundo argumento para que solo en el primer render se asigne el valor del searchParams al input

La hice de esta manera apoyandome de los compañeros: `import React from 'react';` `import { useSearchParams } from 'react-router-dom';` `import './TodoSearch.scss'` `function TodoSearch({``loading``, ``searchValue``, ``setSearchValue``}) {  ` `const [searchParams, setSearchParams] = useSearchParams();  ` `const paramsValue = searchParams.get('search');` `const updateSearchParams = (``value``) => {    setSearchParams({ search: ``value`` });  };  if (paramsValue) {    setSearchValue(paramsValue);  };` `  return (    ` `
      ` `<section ``className``="cont-box">        <input          ` `placeholder``="Busca tu tarea"                ` `value``={paramsValue ?? ''}          ``onChange``=` `{(``event``) => {            const value = ``event``.target.value;            setSearchValue(value);            updateSearchParams(value);          }}          ``disabled``={``loading``}        />` `...` `</section>    ` `
  );}` `export { TodoSearch };`

Aquí mi solución al reto utilizando useSearchParams en typescript y vite:

import { FC, useEffect, Dispatch, SetStateAction, ChangeEvent } from "react";
import './TodoSearch.css';
import { useSearchParams } from "react-router-dom";

interface Props {
    loading?:boolean,
    searchValue: string,
    setSearchValue: Dispatch<SetStateAction<string>>
}

const TodoSearch:FC<Props> = ({
    loading,
    searchValue,
    setSearchValue
}) => {

    const [searchParams, setSearchParams] = useSearchParams();

    
    useEffect(()=>{
        const paramsValue = searchParams.get('search') || '';
        setSearchValue(paramsValue)
    }, [searchParams])

    const onChangeHandler = (event:ChangeEvent<HTMLInputElement>):void => {
        setSearchValue(event.target.value);
        setSearchParams({ search : event.target.value });
    }

    return (
       <input 
        type="text" 
        className="TodoSearch" 
        placeholder="Ingresar nombre de tarea buscada" 
        value={searchValue}
        onChange={onChangeHandler}
        disabled={loading}
    />
    )
}

export { TodoSearch }

En este caso utilize el hook de react-router useSearchParams.
No es la mejor solucion pero aca la tratare de explicar:

Componente TodoSearch:

 const [searchParams, setSearchParams] = useSearchParams({});
let handleSearchChange = (e) => {
        setInputValue(e.target.value)
        setSearchParams({search: e.target.value})
    }

Poniendo dentro del evento change del input setSerachParams nos indica que va a agregar a la ruta una query llamada search que asignara el valor del input

En este punto tambien podemos añadir un evento de tipo enter asi cuando el usuario haga enter el hook useNavigate nos lleve a la ruta asignada.

En el hook useTodos donde tenemos el estado para controlar el inputValue en las validaciones les añadi unos nuevos condicionales… si… la solucion que se me vino es muy tediosa


    if(inputValue.length >= 1){
        arrayOfTodos = todos.filter(todo => todo.text.includes(inputValue))
    }else if(location.search === "?search="){
        console.log('nada que vuscar')
        navigate('/')
    }
    else if(location.search){
        let queryIndex = location.search.indexOf('=') + 1 
        let query = location.search.slice(queryIndex)
        
        setInputValue(query.split('%').map(text => {
            let indexText = text.indexOf('0') + 1
            let newText = text.slice(indexText)
            return newText
        }).join(' '))    
    }else{
        arrayOfTodos = todos
    }
  1. if: Alista en un nuevo array todas las todos que coincidan con lo que el usuario escriba en el input o quiera buscar.
  2. Si el usuario no nos manda nada entonces nos direcciona al home.
  3. Si el location.search existe o si el usuario esta buscando algo: implemente que formatee la query:
    en resumen ej:
    location.search = '/?search=ir%20con%20juandc’
    Lo que esta dentro de este if formatea ese string largo en solamente lo que escriba el usurio en el input y setea el valor del input a lo que nos llega como query
  4. por ultimo el else: nos dice que si no encontro ninguna todo con lo que busca, entonces las todos es a las todos por defecto

No quize aplicar eso en el TodoMachine pero lo implemente aqui
https://briandacampoy.github.io/poke-api/#/

Para resolver el reto tuve que hacer:

  1. Crear un effect para que escuhe cambios en searchValue y navegar al mismo location con un param search
  let location = useLocation();
  let navigate = useNavigate();
  React.useEffect(() => {
    if (searchValue.length > 0) {
      navigate({
        pathname: location.pathname,
        search: `?search=${searchValue}`,
      })
    }
  }, [searchValue])
  1. utilizo el hook useSearchParams de react-router-dom para traer ese valor cuando cambie la ruta
let [searchParams, setSearchParams] = useSearchParams();

const wordFromPath = searchParams.getAll('search')[0];
  1. verifico que el parametro search traiga algun valor y si esa asi hago la busqueda por ese valor y actualizo el valor de searchValue
if (!searchValue.length >= 1) { 

    if(wordFromPath) {
      searchedTodos = todos.filter(todo => {
        const todoText = todo.text.toLowerCase();
        const searchText = wordFromPath.toLowerCase();
        return todoText.includes(searchText);
      });
      setSearchValue(wordFromPath)
    }
    else {
      searchedTodos = todos;
    }

  }

la verdad no se si sea la opcion mas optima ya que por cada cambio de searchValue estoy navegando a la misma pagina, quisiera saber cual seria una mejor opcion

https://nieto35.github.io/personal-page/#/api

Reto completado con Api de git Hub

Se utilizaría la propiedad search del hook useLocation para capturar la query que se le pasa a la url. Teniendo eso se hace el filtrado y se muestra el valor en el input. Y para el otro caso, se utilizaría el useNavigate al hacer submit.

Me suena q es con useLocation 🤔

En APP cree la ruta ```js <Route path="/search/:text" element={<HomePage />} /> ```\<Route path="/search/:text" element={\<HomePage />} /> Luego en TodoSearch/index.js const params = useParams();  const navigate = useNavigate();   const onSearchValueChange = (event) => {    console.log(event.target.value);    navigate( '/search/' + event.target.value);  };   if( params?.text )  {    setSearchValue( params.text );  } ```js const params = useParams(); const navigate = useNavigate(); const onSearchValueChange = (event) => { console.log(event.target.value); navigate( '/search/' + event.target.value); }; if( params?.text ) { setSearchValue( params.text ); } ``` Con esto ya queda con URL bonita, que cambia con el search

Reto: página de búsquedas con navegación

.
El reto es implementar la búsqueda de TODOs por la url; es decir, si mostramos el location.hash mientras escribimos en el buscador de TODOs la url nunca cambia, por lo que el reto consiste en que desde el mismo home #/ cambie la url dependiendo de lo que escriban los usuarios en la búsqueda.
.
En el caso de que no se en encuentren resultados de busqueda, no debería de salir Not Found, sino que debe mostrarse el mismo home pero ya con algo filtrado.
.

Solución 1

.
Para resolver el reto, en el componente TodoSearch vamos a importar los react hooks useNavigate y useSearchParams, estos se utilizan para navegar entre rutas y gestionar los parámetros de búsqueda, respectivamente.
.
Empezamos creando las constantes navigate y searchParams, este último es un elemento que nos permite recuperar el valor de algún parámetro de búsqueda de la url actual; en este caso, el parámetro search y lo guardamos en searchQuery.
.
Utilizamos un useEffect para verificar que si cuando existe searchQuery entonces tengamos que modificar el estado de searchValue por medio de setSearchValue para que tome el valor del searchQuery.
.
Finalmente, en la función onSearchValueChange además de cambiar el valor de searchValue cada vez que escribamos, también cambiaremos la url por navegación hacia la ruta home, pero con ?search=${event.target.value} al final de la misma por medio de la propiedad search del navigate.
.

import React, { useEffect } from "react";
import "./TodoSearch.css";
import { useNavigate, useSearchParams } from "react-router-dom";

function TodoSearch({ searchValue, setSearchValue, loading }) {
  const navigate = useNavigate();
  const [searchParams] = useSearchParams();
  const searchQuery = searchParams.get("search");

  useEffect(() => {
    if (searchQuery) {
      setSearchValue(searchQuery);
    }
  }, [searchValue]);

  const onSearchValueChange = (event) => {
    setSearchValue(event.target.value);
    navigate({
      pathname: "/",
      search: `?search=${event.target.value}`,
      replace: true,
    });
  };

  return (
    <input
      className="TodoSearch"
      placeholder="Cebolla"
      value={searchValue}
      onChange={onSearchValueChange}
      disabled={loading}
    />
  );
}

export { TodoSearch };

Solución 2

.
Otra solución mejor, podría ya no usar useNavigate y netamente utilizar searchParams y setSearchParams de la siguiente manera.
.
A setSearchParams se le puede mandar un objeto con el parámetro de búsqueda y valor correspondiente.
.

import React, { useEffect } from "react";
import "./TodoSearch.css";
import { useSearchParams } from "react-router-dom";

function TodoSearch({ searchValue, setSearchValue, loading }) {
  const [searchParams, setSearchParams] = useSearchParams();
  const searchQuery = searchParams.get("search");

  useEffect(() => {
    if (searchQuery) {
      setSearchValue(searchQuery);
    }
  }, [searchValue]);

  const onSearchValueChange = ({ target: { value } }) => {
    setSearchValue(value);
    setSearchParams({ search: value });
  };

  return (
    <input
      className="TodoSearch"
      placeholder="Cebolla"
      value={searchValue}
      onChange={onSearchValueChange}
      disabled={loading}
    />
  );
}

export { TodoSearch };
A mi se me ocurrió hacer lo siguiente: ```js function TodoSearch({ searchValue, setSearchValue, loading }) { const {searchFilter} = useParams() const navigate = useNavigate() const onChangeSearchValue = (event) => { event.preventDefault() setSearchValue(event.target.value); navigate(`/${event.target.value}`) } if (!loading) { if (searchFilter && !searchValue) { setSearchValue(searchFilter) } } return ( <input placeholder="Hacer curso React.js" className="TodoSearch" value={searchValue} onChange={onChangeSearchValue} disabled={loading} /> ); } ```function TodoSearch({ searchValue, setSearchValue, loading }) { const {searchFilter} = useParams() const navigate = useNavigate() const onChangeSearchValue = (event) => { event.preventDefault() setSearchValue(event.target.value); navigate(`/${event.target.value}`) } if (!loading) { if (searchFilter && !searchValue) { setSearchValue(searchFilter) } } return ( \<input placeholder="Hacer curso React.js" className="TodoSearch" value={searchValue} onChange={onChangeSearchValue} disabled={loading} /> );}
XD Se les fue una letra demás en la url <https://platzi.com/new-home/clases/3468-react-router/51638-reto-pagina-de-busquedas-con-navegacieon/> **navegacieon**