solid-start
solid-start copied to clipboard
useSearchParams() not returning query params within routeData().
The example shown in the Fetching data based on search params section of the useSearchParams() docs suggests we should be able to pass url query parameters to fetcher functions within routeData(). This does not seem to be working. e.g.
export function routeData() {
const [searchParams] = useSearchParams();
return createServerData$(async (query) => {
return hogwarts.search(query)
}, {
key: () => searchParams.query
})
}
My use case is I'm wanting to use hrefs like /?page=3
for paging.
Testing an example it appears the searchParams
value is always undefined
.
Note: This has been tried with createRouteData
and createServerData$
.
Should this example work?
Side note: useSearchParams() works as expected from within the component so am using that as a work around.
Yeah that is odd. Thanks for reporting.
More info... The docs example contains an error. The query parameters are being returned in searchParams not searchParams.query.
Passing searchParams as the key fixes the initial page load but subsequent navigation via <A> tags does not run the fetcher. Console logging shows the key is being retrieved and the searchParams have the correct values. A stacktrace suggests the problem is somewhere in the logic of solid-js/dist/server.cjs that decides whether or not to call the fetcher (somewhere near line 439).
The code for my routeData function can be viewed here. If anyone wants to grab this repo as an example of the issue ensure you use the useSearchParamBug
branch.
Okay solved the issue, and its stupidly intricate. So how the key
works is you need to read all the reactive values you care about in that function and that tells us to rerun the fetcher.
In your current implementation, you use JSON.stringify(searchParams)
and return searchParams
from the key
function. The return is not reading any reactive properties. So it doesn't create any dependency. JSON.stringify
will only read the currently existing properties of searchParams
. So if the page you load has a query param like ?page=1
. It will rerun on page=2
, but not on tag=implementations
because JSON.stringify
what properties will only exist in the future. So nothing is actually subscribed to searchParams.tag
The solution is explicitly reading the searchParams that you want to subscribe to. Even if they will start off as null, the fact that you read that property on searchParams
will make sure when it is present, we have subscribed and we will rerun.
This is also why the example in the docs is:
export function routeData() {
const [searchParams] = useSearchParams();
return createServerData$(async (query) => {
return hogwarts.search(query)
}, {
key: () => searchParams.query
})
}
The fact that we read searchParams.query
means that we are subscribing to the ?query=abc
param. Not all the searchParams. So if you want to subscribe to another query param, like in your case, this is what you would need to do:
export function routeData() {
const [searchParams] = useSearchParams();
const articleData = createRouteData(fetchArticleData, {
key: () => {
console.log(`routeData(): searchParams = ${JSON.stringify(searchParams)}`);
return {
tag: searchParams.tag,
page: searchParams.page,
source: searchParams.source,
};
}
});
}