Open Sartonon opened 2 years ago
Hello,
Have u fixed it? Im having some typescript issues...
Experiencing the same with react-instantsearch 7.1.0
This is still a issue with react-instantsearch 7.7.0
When using this with Nextjs with app router this causesthat the hits array first gets filled with the date fetched fromt the server, then de hits array gets cleared and filled again.
This can cause u see a empty list flashing
We worked around this filling out own hits array outside of useInfiniteHits and clearing it when needed, this is not ideal but it works.
I think the problem here is mainly with the store using the entire state object as the cache key. That object can change a lot, especially during initial page load. For example, disjunctiveFacets
configured at aloglia are loaded async and change the state object. It's a guaranteed cache miss and broken back-button UX unless it's a super simple situation like https://instantsearchjs.netlify.app/stories/js/?path=/story/results-infinitehits--with-sessionstorage-cache-enabled.
What I've done is implement a custom cache that keys by only the aspects of state that should cause a hit/miss. For my needs those are index
optionalFilters
and refinements.
import { SearchParameters } from 'algoliasearch-helper';
import { getRefinements } from 'instantsearch.js/es/lib/utils';
type CacheState = {
hits: InfiniteHitsCachedHits<Product>;
key: string;
};
const cache: InfiniteHitsCache = {
read({ state }) {
const searchParams = SearchParameters.make(state);
const refinements = getRefinements({}, searchParams);
const key = JSON.stringify([
state.index,
state.optionalFilters,
refinements,
]);
const item = sessionStorage.getItem('_infiniteHits');
const hitsCache = item
? (JSON.parse(item) as CacheState)
: null;
return key === hitsCache?.key
? hitsCache.hits
: null;
},
write({ state, hits }) {
const searchParams = SearchParameters.make(state);
const refinements = getRefinements({}, searchParams);
const key = JSON.stringify([
state.index,
state.optionalFilters,
refinements,
]);
sessionStorage.setItem(
'_infiniteHits',
JSON.stringify({
key,
hits,
})
);
},
};
If you only want to use the hits, you could also consider using result.queryId
as the cache key, although that changes if the queries are different.
🐛 Bug description
useInfiniteHits
cache functionality breaks when hooks are used in a certain way for custom search UI components. With components from instantsearch library everything works fine.Cache
read
callback is called multiple times and initially state doesn't include all the facets when coming back to search page and it doesn't match with what is in cache beforewrite
is called and cache resets.https://github.com/algolia/instantsearch.js/blob/master/packages/instantsearch.js/src/lib/infiniteHitsCache/sessionStorage.ts#L29
Example includes custom cache where it is easy to see that
read
function is called multiple times and state is not up to date until later andwrite
gets called before state gets matched from session storage:Console logs when coming back from product page
In the logs you can see that disjunctiveFacets is empty array at first and it won't match with the one in cache.
write
gets called and cache basically resets.<InstantSearchFilters />
can be uncommented to check working functionality🔍 Bug reproduction
Steps to reproduce the behavior:
Live reproduction:
https://codesandbox.io/s/musing-cori-g9895q
💭 Expected behavior
When more products have been fetched with "Show more" button and user navigates to a product page and back, search results should still have all the previously fetched products visible.
Environment