React-instantsearch: Instantsearch server-side rendering is also fetching on the client

Created on 11 Aug 2020  路  16Comments  路  Source: algolia/react-instantsearch

Describe the bug 馃悰

Maybe is because the mock request I'm doing for empty query. I don't know the cause.

To Reproduce 馃攳

  1. Go to https://sgk4r.sse.codesandbox.io/?query=amazon&page=1&configure%5BhitsPerPage%5D=4
  2. Open the Chrome console and refresh the page
  3. Go to Network tab and see the queries 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 馃枼

image

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

鉂わ笍 Bug

All 16 comments

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:

  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

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}
          >
Was this page helpful?
0 / 5 - 0 ratings

Related issues

flouc001 picture flouc001  路  5Comments

developerk786 picture developerk786  路  3Comments

vinhlh picture vinhlh  路  5Comments

flouc001 picture flouc001  路  5Comments

hatched-danny picture hatched-danny  路  3Comments