'How can i stop this infinite loop in my next.js app? (Search page via url params)
I want to create a search page that in driven by the url query. It includes params such as:
- searchQuery e.g. "nike shoes"
- page e.g. "1"
etc.
However im struggling at the moment with getting it work without heading into an infinite loop.
Here is my search page:
function Search() {
const [listings, setListings] = useState<null | Array<ListingObject>>(null);
const [noResultsListings, setNoResultsListings] = useState<
Array<ListingObject>
>([]);
const [loading, setLoading] = useState("idle");
const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false);
const [paginationData, setPaginationData] = useState<PaginationData>({
totalHits: 0,
totalPages: 0,
});
const { query } = Router;
function onFilterCheck(checked: Boolean, id: string, option) {
let query = Router.query;
if (checked) {
if (query[id]) {
query[id] += `,${option.value}`;
} else {
query[id] = `${option.value}`;
}
}
if (!checked) {
if (query[id] && query[id].toString().split(",").length > 1) {
let param = query[id].toString();
let array = param.split(",");
query[id] = array.filter((param) => param !== option.value);
} else {
delete query[id];
}
}
Router.push(
{
pathname: `/search`,
query,
},
undefined,
{ shallow: true }
);
}
const onSortByChanged = (sortByIndex: string) => {
let { query } = Router;
if (sortByIndex) {
query.sortBy = sortByIndex;
Router.push(
{
pathname: `/search`,
query,
},
undefined,
{ shallow: true }
);
}
};
const search = useCallback(async (query) => {
console.log("in search");
if (loading === "idle") {
console.log("inside IF");
setLoading("loading");
const readyQuery = await handleSearchQuery(query);
let listingIndex = searchClient.initIndex(query.sortBy ?? "listings");
const response = await listingIndex.search(readyQuery.searchQuery, {
numericFilters: readyQuery.filters.numericFilters,
facetFilters: readyQuery.filters.facetFilters,
page: query.page - 1 ?? 0,
});
if (response) {
const hits = response.hits;
setPaginationData({
totalPages: response.nbPages,
totalHits: response.nbHits,
});
if (hits.length === 0) {
const response = await getNewListings(3);
if (response.success) {
setNoResultsListings(response.listings);
}
}
setListings(hits);
}
setLoading("idle");
}
}, []);
const handlePageClick = useCallback(
(pageNumber) => {
console.log("page number from handlePageClick", pageNumber);
if (loading === "idle") {
let { query } = Router;
query.page = (parseInt(pageNumber) + 1).toString();
Router.push(
{
pathname: `/search`,
query,
},
undefined,
{ shallow: true }
);
search(query);
window.scrollTo(0, 0);
}
},
[loading, search]
);
useEffect(() => {
search(query);
}, [search, query]);
useEffect(() => {
if (process.env.NODE_ENV === "production") {
hotjar.initialize(
parseInt(process.env.NEXT_PUBLIC_HOTJAR_ID),
parseInt(process.env.NEXT_PUBLIC_HOTJAR_SV)
);
}
}, []);
if (!Router.query.page) {
const { query } = Router;
query.page = "1";
Router.push({ pathname: "/search", query }, undefined, {
shallow: true,
});
}
return (
<main className="mx-auto w-full transition-all duration-200 ease-in md:px-0">
<section className="flex w-full flex-col pt-0 md:flex-row">
<div className="hidden min-h-screen w-1/5 grid-cols-1 md:grid lg:grid-cols-1 lg:pr-4">
{/* Desktop filters */}
<form className="hidden h-full overflow-y-auto p-0 md:inline-block">
<div className="relative z-30 flex h-full flex-col border-r pb-6 pr-6 md:z-0">
<SearchSortBy onSortByChanged={onSortByChanged} />
<SearchFilters
mobileFiltersOpen={mobileFiltersOpen}
setMobileFiltersOpen={() => {
setMobileFiltersOpen(false);
}}
onFilterCheck={onFilterCheck}
/>
</div>
</form>
</div>
<span className="flex items-center justify-between">
<SearchSortBy
className="md:hidden"
onSortByChanged={onSortByChanged}
/>
<Button
className="!px-4 md:hidden"
variant="none"
type="button"
onClick={() => {
setMobileFiltersOpen(true);
}}
>
<FilterIcon className="w-4 text-blue-600" />
</Button>
</span>
<SearchResultsGrid
listings={listings}
noResultsListings={noResultsListings}
/>
</section>
{listings?.length > 0 && (
<SearchPagination
pageNumber={parseInt(Router?.query?.page.toString()) - 1}
paginationData={paginationData}
handlePageClick={handlePageClick}
/>
)}
</main>
);
}
export default Search;
If I search something on my site, the console will look like this:
in search
Search.tsx?c6ec:94 inside IF
Search.tsx?c6ec:92 in search
Search.tsx?c6ec:94 inside IF
Search.tsx?c6ec:92 in search
Search.tsx?c6ec:92 in search
Search.tsx?c6ec:94 inside IF
Search.tsx?c6ec:92 in search
Search.tsx?c6ec:92 in search
Search.tsx?c6ec:94 inside IF
Search.tsx?c6ec:92 in search
Search.tsx?c6ec:92 in search
Search.tsx?c6ec:94 inside IF
Search.tsx?c6ec:92 in search
Search.tsx?c6ec:92 in search
Search.tsx?c6ec:94 inside IF
Search.tsx?c6ec:92 in search
Search.tsx?c6ec:92 in search
Search.tsx?c6ec:94 inside IF
I tried using loading
as a gate, where it will only search if it is equal to idle
, however I change the the loading state at the end of the search back to idle
, therefore it triggers a loop.
Does anybody have any experience in how I will handle this? Is adding the router query to a useEffect
to fire the search the right way to go? Or should I just pass the search function to all the things that need it?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|