No tienes acceso a esta clase

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

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

26/30
Recursos

Aportes 14

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

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 🤔

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