react-instantsearch
react-instantsearch copied to clipboard
Instantsearch server-side rendering is also fetching on the client
Describe the bug 🐛
Maybe is because the mock request I'm doing for empty query. I don't know the cause.
To Reproduce 🔍
- Go to https://sgk4r.sse.codesandbox.io/?query=amazon&page=1&configure%5BhitsPerPage%5D=4
- Open the Chrome console and refresh the page
- Go to
Network
tab and see thequeries
request made on the client
A live example helps a lot! :100:
Your application on codesandbox does not seem to include the code from the guide, are you saying it's solved or not? thanks Originally posted by @Haroenv in https://github.com/algolia/react-instantsearch/issues/2960#issuecomment-671830911
Here's a codesandbox: https://codesandbox.io/s/nextjs-algolia-instantsearch-sgk4r
Expected behavior 💭
A clean rendering ONLY on the server-side and see the HTML appropriately on the page.
Screenshots 🖥
Environment:
- OS:
Linux debian 4.9.0-13-amd64
- Google Chrome Browser
Version 84.0.4147.125 (Official Build) (64-bit)
- NPM:
6.14.4
- Node.js:
10.20.1
- next:
9.3.5
- react:
16.13.1
- algoliasearch:
4.2.0
- react-instantsearch-dom:
6.6.0
Additional context
- Here's the doc I followed: algolia.com - building search UI - conditional requests
- Related: issue #2960 (Error serializing
.resultsState.state
returned fromgetServerSideProps
in "/search")
Hmm, I originally thought that this could be due to the fact that the custom client for conditional requests no longer has the cache functionality, and can't be hydrated. If that was the case, the solution would be:
const searchClientSearch = searchClient.search;
searchClient.search = function search(requests) {
if (requests.every(({ params }) => !params.query)) {
return Promise.resolve({
results: requests.map(() => ({
hits: [],
nbHits: 0,
nbPages: 0,
processingTimeMS: 0
}))
});
}
return searchClientSearch(requests);
};
as in https://codesandbox.io/s/nextjs-algolia-instantsearch-forked-wh7cu?file=/modules/instantsearch/services.js
Unfortunately that does not seem to be the cause, so further investigation is needed why the cache isn't hit 🤔
I can see it search on server-side the first time, than in a second or two, starts fetching on queries?x-algolia-agent=.....
two times on the client.
I look forward to see it fixed. :100:
This does not seem to be an issue when using getInitialProps
(as used in the example here). Is there any chance this is related to this issue?
Do you also have an issue @callmephilip? Would love to see another (more minimal) example of what could be going on
@Haroenv i have a reproduction here
- basically it boils down to wrapping a helper component in
connectStats
(we were using this to adjust how filters are rendered based on the number of search results + for adjusting some UI elements)
Ah yes that makes sense, the stats will not render on the server, since there's no results yet, and thus it will not render the refinements either. Since it doesn't render, the widgets will not be taken in account for server side rendering, and the state won't be the same on the client as on the server.
An easy workaround is to run findResultsState
twice, which is an extra search on the server, or alternatively I think it might be possible to work something out where you pass resultsState to findResultsState already with a basic state that will cause stats to render.
Thanks for the reproduction @callmephilip, let my know if my suggestions make sense / work!
@Haroenv i don't think i follow completely.
looking at this here
static async getInitialProps({ asPath }) {
const searchState = pathToSearchState(asPath);
const resultsState = await findResultsState(App, {
...DEFAULT_PROPS,
searchState,
});
return {
resultsState,
searchState,
};
}
resultsState
should already contain all the information necessary to pull stats for the search. here's a snapshot from the server log:
{ metadata:
[ { id: 'query', index: 'instant_search', items: [Array] },
{ id: 'categories', index: 'instant_search', items: [] },
{ id: 'page' } ],
rawResults:
[ { hits: [Array],
nbHits: 21469,
page: 0,
nbPages: 84,
hitsPerPage: 12,
facets: [Object],
exhaustiveFacetsCount: true,
exhaustiveNbHits: true,
query: '',
queryAfterRemoval: '',
params:
'highlightPreTag=%3Cais-highlight-0000000000%3E&highlightPostTag=%3C%2Fais-highlight-0000000000%3E&hitsPerPage=12&query=&maxValuesPerFacet=10&page=0&facets=%5B%22categories%22%5D&tagFilters=',
index: 'instant_search',
processingTimeMS: 3 } ],
state:
SearchParameters {
facets: [],
disjunctiveFacets: [ 'categories' ],
hierarchicalFacets: [],
facetsRefinements: {},
facetsExcludes: {},
disjunctiveFacetsRefinements: {},
numericRefinements: {},
tagRefinements: [],
hierarchicalFacetsRefinements: {},
index: 'instant_search',
highlightPreTag: '<ais-highlight-0000000000>',
highlightPostTag: '</ais-highlight-0000000000>',
hitsPerPage: 12,
query: '',
maxValuesPerFacet: 10,
page: 0 } }
@Haroenv any updates on this?
Hi @callmephilip, sorry, I missed your response! I think nested connectors in this way basically don't render their children if no results have yet been set, since the flow is:
- findResultsState
- internal render to find all components
- stats doesn't render its children, since there's not yet results
- children of stats don't render
- search happens without stats' children
- main render happens
A solution could be making a custom version of connectStats which does render if there's no results
Hi @callmephilip, sorry, I missed your response! I think nested connectors in this way basically don't render their children if no results have yet been set, since the flow is:
1. findResultsState 2. internal render to find all components 3. stats doesn't render its children, since there's not yet results 4. children of stats don't render 5. search happens without stats' children 6. main render happens
A solution could be making a custom version of connectStats which does render if there's no results
Stumbled upon this issue too: https://codesandbox.io/s/flamboyant-sanderson-kx6ns?file=/pages/search.js
Hi @callmephilip, sorry, I missed your response! I think nested connectors in this way basically don't render their children if no results have yet been set, since the flow is:
1. findResultsState 2. internal render to find all components 3. stats doesn't render its children, since there's not yet results 4. children of stats don't render 5. search happens without stats' children 6. main render happens
A solution could be making a custom version of connectStats which does render if there's no results
Error: Error serializing `.initialResultsState.metadata[0].items[0].value` returned from `getServerSideProps` in "/search". Reason: `function` cannot be serialized as JSON. Please only return JSON serializable data types.
It's undefined so it can't be parsed.
So I did a production build and the build completed without any errors. I also navigated on the page and it works too. It's broken in development only.
What's the point of passing the component into the getResultsState function.
I narrowed it down to:
const singleIndexSearch = (helper, parameters) => helper.searchOnce(parameters).then((res) => ({
rawResults: cleanRawResults(res.content._rawResults),
state: res.content._state,
}))
I'm guessing ServerSideProps works differently than getInitialProps. The first render with ServerSideProps is resultsState is undefined therefore it errors out. I just added an initialState to resultsState and it works.
Demo: https://codesandbox.io/s/practical-frog-y0thh?file=/pages/index.js
Code
Search.js file
const resultsState = initialResultsState || {
metadata: [
{
id: "",
index: "",
items: [
{
label: "",
currentRefinement: "",
},
],
},
{
id: "",
},
],
rawResults: [
{
index: "",
hitsPerPage: 0,
exhaustiveNbHits: false,
nbHits: 0,
processingTimeMS: 0,
query: "",
nbPages: 0,
page: 0,
hits: [],
},
],
state: {},
};
return <InstantSearch
searchClient={searchClient}
resultsState={resultsState}
onSearchStateChange={onSearchStateChange}
searchState={searchState}
createURL={createURL}
{...DEFAULT_PROPS}
widgetsCollector={widgetsCollector}
{...resProps}
>
Hi! As React InstantSearch Hooks does not have this behavior, we decided not to add new features/fixes to this feature in React InstantSearch. Since this issue seems not to have generated much activity lately, so we're going to close it, feel free to reopen if needed.