Suspense not updating when search params get updated using next/navigation
Unanswered
πΉπΎ_π·πππππππππππ π posted this in #help-forum
I have this component A for filter on which I have added a suspense and fetching is done inside this component A. Inside this component I have used a checkbox which when checked replace the url using the next/navigation:
When this url updates then again the fetching is done which should show the fallback component but it' s not showing the fallback component
replace(`${pathName}?${params.toString()}`, { scroll: false });When this url updates then again the fetching is done which should show the fallback component but it' s not showing the fallback component
26 Replies
@πΉπΎ_π·πππππππππππ π I have this component A for filter on which I have added a suspense and fetching is done inside this component A. Inside this component I have used a checkbox which when checked replace the url using the next/navigation:
replace(`${pathName}?${params.toString()}`, { scroll: false });
When this url updates then again the fetching is done which should show the fallback component but it' s not showing the fallback component
set a key to the
<Suspense /><Suspense key={params.toString()} />I tried adding a key to the suspense like this(searchParams is the object providing the whole url slugs as well as query params:
Still it's not working.
<Suspense key={JSON.stringify(searchParams)} />Still it's not working.
When I use replace to change the url on clickiing the checkbox its taking quite some time to update the url and after few seconds when the url updates, the data comes at that moment. Meaning , there is some delay to the next/navigation when updating the url.
@πΉπΎ_π·πππππππππππ π When I use replace to change the url on clickiing the checkbox its taking quite some time to update the url and after few seconds when the url updates, the data comes at that moment. Meaning , there is some delay to the next/navigation when updating the url.
does the suspense work on page load?
Yes, whenever I hard refresh the page, the suspense is working but when updating the url through next/navigation (replace method) it's not working. When I click on the checkbox, it updates the url in 2-3 seconds (meaning there is a delay in updating the url) and once the url updates then the data comes within that time(no delay meaning within few milliseconds)
@πΉπΎ_π·πππππππππππ π Yes, whenever I hard refresh the page, the suspense is working but when updating the url through next/navigation (replace method) it's not working. When I click on the checkbox, it updates the url in 2-3 seconds (meaning there is a delay in updating the url) and once the url updates then the data comes within that time(no delay meaning within few milliseconds)
how do you render Suspense and the component A?
and where is the fetching happen?
This is how I have used the component A which for this is CountryFilter :
And inside CountryFilter:
{shouldShowCountries && (
<Suspense fallback={<FilterItemSkeleton />}>
<li>
<CountryFilter
key={JSON.stringify(searchParams)}
params={searchParams}
/>
</li>
</Suspense>
)}And inside CountryFilter:
import {
AggregateSearchFilter,
fetchAggregateCountries,
} from "@/app/data/product-search";
import { ProductSearchParams } from "@/types/search";
import { FunctionComponent } from "react";
import CountryFilter from "./CountryList";
interface CountryFilterContainerProps {
params: ProductSearchParams;
}
const CountryFilterContainer: FunctionComponent<
CountryFilterContainerProps
> = async ({ params }) => {
const countriesData: AggregateSearchFilter[] =
await fetchAggregateCountries(params);
return (
<>
{countriesData && countriesData.length !== 0 && (
<CountryFilter countries={countriesData} />
)}
</>
);
};
export default CountryFilterContainer;@πΉπΎ_π·πππππππππππ π This is how I have used the component A which for this is CountryFilter :
js
{shouldShowCountries && (
<Suspense fallback={<FilterItemSkeleton />}>
<li>
<CountryFilter
key={JSON.stringify(searchParams)}
params={searchParams}
/>
</li>
</Suspense>
)}
And inside CountryFilter:
js
import {
AggregateSearchFilter,
fetchAggregateCountries,
} from "@/app/data/product-search";
import { ProductSearchParams } from "@/types/search";
import { FunctionComponent } from "react";
import CountryFilter from "./CountryList";
interface CountryFilterContainerProps {
params: ProductSearchParams;
}
const CountryFilterContainer: FunctionComponent<
CountryFilterContainerProps
> = async ({ params }) => {
const countriesData: AggregateSearchFilter[] =
await fetchAggregateCountries(params);
return (
<>
{countriesData && countriesData.length !== 0 && (
<CountryFilter countries={countriesData} />
)}
</>
);
};
export default CountryFilterContainer;
<Suspense key={JSON.stringify(searchParams)} fallback={<FilterItemSkeleton />}>
<li>
<CountryFilter
params={searchParams}
/>
</li>
</Suspense>set the key on suspense not on CountryFilter
it is indeed in the Suspense not in the CountryFilter component:
Sorry, I copied the wrong code here... But yes, i have used the key on suspense like this:
<Suspense key={JSON.stringify(searchParams)} fallback={<FilterItemSkeleton />}>Sorry, I copied the wrong code here... But yes, i have used the key on suspense like this:
{shouldShowCountries && (
<Suspense
key={JSON.stringify(searchParams)}
fallback={<FilterItemSkeleton />}
>
<li>
<CountryFilter params={searchParams} />
</li>
</Suspense>
)}This issue is happening whenever I try to update the url in a client component using next/navigation like this in my checkbox component :
/* eslint-disable react-hooks/exhaustive-deps */
"use client";
import { AggregateSearchFilter } from "@/app/data/product-search";
import Checkbox from "@/components/commons/Checkbox";
import { FilterParams } from "@/constants/search";
import useSearchFilter from "@/hooks/useSearchFilter";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { ChangeEvent, FunctionComponent, useEffect, useState } from "react";
const CountryCheckbox: FunctionComponent<{
country: AggregateSearchFilter;
}> = ({ country }) => {
const searchParams = useSearchParams();
const { replace } = useRouter();
const pathName = usePathname();
const isCountrySelected = searchParams
.getAll(FilterParams.COUNTRIES)
.includes(country.id.toString());
const [isChecked, setIsChecked] = useState<boolean>(isCountrySelected);
const { addToSelectedCountries, removeFromSelectedCountries } =
useSearchFilter();
useEffect(() => {
if (isCountrySelected) {
setIsChecked(true);
addToSelectedCountries(country);
} else {
setIsChecked(false);
removeFromSelectedCountries(country.id);
}
}, [country, isCountrySelected]);
const handleSearch = (e: ChangeEvent<HTMLInputElement>) => {
... some code
replace(`${pathName}?${params.toString()}`, { scroll: false });
};
return (
<Checkbox
value={country.id}
label={country.name}
onChange={handleSearch}
checked={isChecked}
/>
);
};
export default CountryCheckbox;There is still an issue open on next.js repository on github:
https://github.com/vercel/next.js/issues/53543
This is what's happening I think
https://github.com/vercel/next.js/issues/53543
This is what's happening I think
@πΉπΎ_π·πππππππππππ π it is indeed in the Suspense not in the CountryFilter component:
js
<Suspense key={JSON.stringify(searchParams)} fallback={<FilterItemSkeleton />}>
Sorry, I copied the wrong code here... But yes, i have used the key on suspense like this:
js
{shouldShowCountries && (
<Suspense
key={JSON.stringify(searchParams)}
fallback={<FilterItemSkeleton />}
>
<li>
<CountryFilter params={searchParams} />
</li>
</Suspense>
)}
can you try render this to your page
<Suspense
key={JSON.stringify(searchParams)}
fallback={<div>loading...</div>}
>
<Test />
</Suspense>
async function Test() {
await new Promise((resolve) => setTimeout(resolve, 2000));
return <div>Test</div>;
}and try clicking the checkbox
Nope, it's not working...When I refresh the page then the Loading shows but later when I click on the checkbox it's not working... I think this is the similar issue I am currently facing with:
https://github.com/vercel/next.js/issues/
https://github.com/vercel/next.js/issues/
I am on next@14.1.3 but this issue has been since few months for me...still no solution... First, even on reloading the page suspense didn't used to work which was the issue of not adding key as next doesn't include the searchParams in the key of a route so navigation ot the same route do not retrigger the suspense fallback
Now, this has been a major issue for me as fetching new data doesn't load any loaders or fallback component to show which is very bad for Ui/Ux
@πΉπΎ_π·πππππππππππ π I am on next@14.1.3 but this issue has been since few months for me...still no solution... First, even on reloading the page suspense didn't used to work which was the issue of not adding key as next doesn't include the searchParams in the key of a route so navigation ot the same route do not retrigger the suspense fallback
do you see a network request is made when you click the checkbox
Yes, it sure does make a network request when clicking on the checkbox:
could you share the repo or create a repoduction?
Yes, I did check using the timeout of 2 seconds and indeed it's working and fallback component is showing but still there is some delay in showing the fallback component when I do like this :"
import { ProductSearchParams } from "@/types/search";
import { FunctionComponent } from "react";
import DepartureDateList from "./DepartureDateList";
import { fetchAggregateDepartureMonths } from "@/app/data";
const DestinationFilterContainer: FunctionComponent<{
params: ProductSearchParams;
}> = async ({ params }) => {
await new Promise((resolve) => setTimeout(resolve, 2000));
const { data: departureMonths } = await fetchAggregateDepartureMonths(params);
return (
<>
{departureMonths && departureMonths.length !== 0 && (
<DepartureDateList departureMonths={departureMonths} />
)}
</>
);
};
export default DestinationFilterContainer;This is how fetching is done :
Also in the buildProductSearchQueryParams its just updating url as per the params which are available
export const fetchAggregateDepartureMonths = async (
params: AggregateSearchParams,
) => {
const url = buildProductSearchQueryParams({
params,
url: new URL(API_GET_PRODUCT_SEARCH_AGGREGATE_DEPARTURE_MONTHS),
});
Logger.info(`Fetching aggregate departure months ${url.toString()}`);
const response = await fetch(url.toString(), { cache: "no-store" });
if (!response.ok) {
const error = new Error(response.statusText, { cause: response?.status });
Logger.error(`Failed to fetch departure months`, error);
throw error;
}
return response.json() satisfies Promise<AggregateSearchFilter[]>;
};Also in the buildProductSearchQueryParams its just updating url as per the params which are available
Also, does this means we cannot use suspense on next/navigation or links ???: