Utilizo un useEffect en mi código React que funciona bien al principio, pero al escribir un motor de búsqueda usando un estado de uso llamado "búsqueda" al establecer su nuevo valor a través de una entrada en Cambiar, mi efecto de uso se ejecuta automáticamente nuevamente, aunque nunca está esperando cambiar mi estado de uso a " buscar", este es mi código
const [search, setSearch] = useState(""); useEffect(() => { pages(); }, [limitPage, page, products]); const pages = () => { // eslint-disable-next-line getProducts(limitPage, page, search); getXlsProducts(100000, 1); const totalPages = Math.ceil(totalDocs / limitPage); const links = []; for (let i = 1; i <= totalPages; i++) { links[i] = i; } setNumbers(links); setTotalDocs(products.products?.totalDocs); setTotalPages(products.products?.totalPages); setCurrentPage(products.products?.page); setNextPage(products.products?.hasNextPage); setPreviousPage(products.products?.hasPrevPage); };Necesito usar el botón que llama a la función handleSearch() para realizar la búsqueda, pero tan pronto como ocurre automáticamente un cambio en la entrada, se ejecuta mi useEffect:
<div> <input className="border-1 border-2 border-solid rounded border-slate-300" value={search} onChange={(e) => setSearch(e.target.value)} /> <button onClick={() => handleSearch()}> <FiSearch /> </button> </div>Aquí está la función que se debe usar al hacer clic en el botón, pero nunca se usa porque tan pronto como la búsqueda cambia el valor a través de la entrada, ya no se ejecuta:
const handleSearch = () => { setLimitPage(10); setPage(1); getProducts(limitPage, page, search); };¿Cómo puedo evitar el disparo automático de mi useEffect para poder usar mi función de búsqueda a través del botón? Gracias.
useEffect(() => { pages(); }, [limitPage, page, products]);Las dependencias en los ganchos de React usan una comparación superficial .
Como @ChrisG mencionó en el comentario: "Tienes un bucle infinito". Es un bucle asíncrono porque, como dijiste, la función getProducts solicita productos del servidor. Entonces, cuando obtiene una lista de productos del servidor, incluso si son semánticamente iguales a los ya establecidos, aún crea una nueva matriz con una nueva referencia. ¡Entonces useEffect se activó nuevamente porque la referencia en la lista de productos ha cambiado !
//🥇triggered useEffect by first run // when we create component and call `pages()` useEffect(() => { pages(); }, [limitPage, page, products]); const pages = () => { //🥈Make a request to the server and set products list // like setProducts(response.products) this cause // create new array with new reference getProducts(limitPage, page, search); ... } //🥉products === response.products // False because these are two diffirent objects // so we trigger useEffect again (returns to 🥈) useEffect(() => { pages(); }, [limitPage, page, products]);Por lo que he entendido, desea que se llame a getProducts() en el first render , así como siempre que cambie la limitPage and page estado y la página y cada vez que se presione el botón de Search .
Ya tiene la función handleSearch para llamar a getProducts() que está activando la API al hacer clic en el botón Search ,
const handleSearch = () => { setLimitPage(10); setPage(1); getProducts(limitPage, page, search); }; En handleSearch() como lo menciona @ChrisG en el comentario: debe cuidar limitPage y el valor de la page , ya que utilizará el valor anterior aquí.
para llamar a la API en el first render y siempre que los estados limitPage y la page cambien, elimine la dependencia de los products de useEffect y modifique la función pages() con las declaraciones requeridas:-
useEffect(() => { pages(); }, [limitPage, page]); const pages = () => { // eslint-disable-next-line getProducts(limitPage, page, search); getXlsProducts(100000, 1); const totalPages = Math.ceil(totalDocs / limitPage); const links = []; for (let i = 1; i <= totalPages; i++) { links[i] = i; } setNumbers(links); }; Vi varios estados que desea actualizar sobre el cambio de estado de los products , para eso, puede tener otro useEffect de uso con dependencia de products y un nuevo método que actualiza los estados requeridos: -
useEffect(() => { updateStates(); }, [products]); const updateStates = () => { setTotalDocs(products.products?.totalDocs); setTotalPages(products.products?.totalPages); setCurrentPage(products.products?.page); setNextPage(products.products?.hasNextPage); setPreviousPage(products.products?.hasPrevPage); };El problema es la comparación superficial según lo declarado por @maksimr. Una alternativa es usar componentes basados en clases con shouldComponentUpdate :
shouldComponentUpdate(nextProps, nextState) { // if no products in current state, update when there are products in // future state, or else remain. if(!this.state.products.length) { return nextState.products.length; } // update if there's a product in current state whose id // is not found in the next state's products return this.state.products.some((prod)=>( !nextState.products.find((p)=> ( prod.id == p.id ) ); }