Skip to content
Advertisement

I can fetch data based on my query, but then the fetching never stops. How do I limit the fetch requests?

As I type in my search, the data will fetch from the API properly. The problem is that it keeps fetching several thousand times until the API throws an error. I successfully implemented an abortController, but I feel like there’s a less abrupt way to stop a fetch. I am using the useEffect hook based on the query and inventory state, but I don’t think my logic is very concise at all. Ideally, I need to fetch the data once and wait to fetch it again when the query state changes. Not necessarily the query and inventory as it is in the code, but that’s the only way it seemed to work.

Note: If there is nothing in the query, no results should be displayed.

Here’s the API (not sure if it’s needed for this question):

export const search = (query, maxResults) =>
  fetch(`${api}/search`, {
    method: "POST",
    headers: {
      ...headers,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ query, maxResults }),
  })
    .then((res) => res.json())
    .then((data) => data.books);

Here’s the Search component:

import { Link } from "react-router-dom";
import { useEffect, useState } from "react";
import * as BooksAPI from "./BooksAPI";

const SearchBooks = () => {
    const [query, setQuery] = useState("");
    const [bookInventory, setBookInventory] = useState([]);

    useEffect(() => {
        let abortFetch = false;

        const getBooks = async () => {
            if (query.length !== 0) {
                const res = await BooksAPI.search(query, 20);
                setBookInventory(res);
            } 
            if (query.length === 0) {
                setBookInventory();
            }
        };

        if (!abortFetch) {
            getBooks();
        }
        
        return () => {
            abortFetch = true;
        }
    }, [query, bookInventory])

    const updateSearchQuery = (query) => {
        setQuery(query);
    };

    return (
        <div className="search-books">
            <div className="search-books-bar">
                <Link to={"/"} className={"close-search"}>Close</Link>
                <div className="search-books-input-wrapper">
                    <input
                        type="text"
                        placeholder="Search by title, author, or ISBN"
                        value={query}
                        onChange={(e) => updateSearchQuery(e.target.value)}
                    />
                </div>
            </div>
            <div className="search-books-results">
                {
                typeof bookInventory === 'object' && bookInventory.length > 0
                    ? <p>Your search results go here</p>
                    : <p>Enter a valid search term to see results.</p>
                }
            </div>
        </div>
    )
}

export default SearchBooks;

Advertisement

Answer

Because you have put bookInventory in Second argument to useEffect, and put setBookInventory in useEffect function. This means every time bookInventory changes, call useEffect.
This’s the reason of that it keeps fetching.

The solution is to delete bookInventory from Second argument to useEffect:

useEffect(() => {
    ...
}, [query]);
User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement