How to prevent reloading everything on a page when a query param changes?
Unanswered
Lilac posted this in #help-forum
LilacOP
I have a page with two components on it, that fetch data from different sources (each wrapped in Suspense). One of them (Data A) is data that is static in the sense that the user can not filter / search through it. The other one (Data B) is a list of items that can be filtered and searched. The filters / search query are stored in the query params and I update the query params using router.replace. My problem is that every time I change a filter, Data A is reloaded although it does not need to be. I think triggering a navigation using replace makes the whole page rerender, which makes sense but I don't know how to prevent Data A from reloading.
const ComponentWithDataA = () => {
const dataAPromise = getDataA();
return (
<Suspense fallback={null}>
<DisplayDataAComponent dataAPromise={dataAPromise} />
</Suspense>
);
};
const ComponentWithDataB = ({ filters }) => {
const dataBPromise = getDataB(filters);
return (
<Suspense key={JSON.stringify(filters)} fallback={null}>
<DisplayDataBComponent dataBPromise={dataBPromise} />
</Suspense>
);
};
const Page = async ({ params }) => {
return (
<div>
<ComponentWithDataA />
<div>
<ComponentBFilters />
<ComponentWithDataB filters={params} />
</div>
</div>
);
};
export default Page;6 Replies
@Lilac I have a page with two components on it, that fetch data from different sources (each wrapped in Suspense). One of them (Data A) is data that is static in the sense that the user can not filter / search through it. The other one (Data B) is a list of items that can be filtered and searched. The filters / search query are stored in the query params and I update the query params using router.replace. My problem is that every time I change a filter, Data A is reloaded although it does not need to be. I think triggering a navigation using replace makes the whole page rerender, which makes sense but I don't know how to prevent Data A from reloading.
tsx
const ComponentWithDataA = () => {
const dataAPromise = getDataA();
return (
<Suspense fallback={null}>
<DisplayDataAComponent dataAPromise={dataAPromise} />
</Suspense>
);
};
const ComponentWithDataB = ({ filters }) => {
const dataBPromise = getDataB(filters);
return (
<Suspense key={JSON.stringify(filters)} fallback={null}>
<DisplayDataBComponent dataBPromise={dataBPromise} />
</Suspense>
);
};
const Page = async ({ params }) => {
return (
<div>
<ComponentWithDataA />
<div>
<ComponentBFilters />
<ComponentWithDataB filters={params} />
</div>
</div>
);
};
export default Page;
instead of reloading everything you can reload only the filtered list. You said the problem correctly: when pushing to a new route the page will be reloaded and that happens also with searchParams.
1. The solution is simple: create a client component, that loads your list and also contains a search fields. This search field executes a function that returns the filtered list (when searched). Then just replace your new results with the current data and the rerender happens thought react. Data A then won*t reload.
2. Another solution would be to use third party libraries like nuqs. They provide a way to push searchParams without actually reloading the page. However: it can be buggy and there still need to be a client component that receives this update and than does the same: fetch new data and repplace & rerender the current list.
I suggest using method 1.
1. The solution is simple: create a client component, that loads your list and also contains a search fields. This search field executes a function that returns the filtered list (when searched). Then just replace your new results with the current data and the rerender happens thought react. Data A then won*t reload.
2. Another solution would be to use third party libraries like nuqs. They provide a way to push searchParams without actually reloading the page. However: it can be buggy and there still need to be a client component that receives this update and than does the same: fetch new data and repplace & rerender the current list.
I suggest using method 1.
LilacOP
Thanks. Basically means this is very hacky to do with server components and there is no official / state of the art solution (except opting out of RSC) for this problem at this point?
@Lilac Thanks. Basically means this is very hacky to do with server components and there is no official / state of the art solution (except opting out of RSC) for this problem at this point?
you can still use RSC: load initial data (initial list in your case) from the server and pass it to your client component. The client component then replaces it with the search results (when the user searches)
Siberian Flycatcher
Can you move
Layouts don't re-render on route change.
<ComponentWithDataA /> to the layout?Layouts don't re-render on route change.
@Siberian Flycatcher Can you move `<ComponentWithDataA />` to the layout?
Layouts don't re-render on route change.
that wouldnt solve the initial problem. He want to reload, because of new results
@Lilac solved?