Algolia vue-instantsearch firing same query multiple times

I’m trying to implement algolia with vue in a website and it’s working fine for the most part, however every time I try to perform a search using a custom input field it fires twice per keystroke and it’s causing our search operations to double for every use in the site.

As an example here’s how the problematic code looks like:

HTML

<div id="instant-search">
  <div class="main container instant-search">
    <ais-instant-search :query="query" :search-client="algoliaClient" index-name="instant_search">
      <ais-configure :query="query" :hits-per-page.camel="21" :min-word-sizefor-1-typo.camel="6"></ais-configure>
      <ais-search-box></ais-search-box>
      <input v-model="query" />
      <ais-hits></ais-hits>
  </div>
</div>

JS

var vm = new Vue({
      el: '#instant-search',
      data: function() {
        return {
          algoliaClient: algoliasearch(
            'ALGOLIA_APP_ID',
            'ALGOLIA_API_KEY'
          ),
          query: '',
        };
      },
});

If you open the Network tab and try typing into the <input v-model="query" /> field you’ll see 2 requests going to Algolia however doing the same in the <ais-search-box> field shows a single request for every keystroke which expected.

Is there a way to implement the same behaviour from ais-search-box into my input field? The reason I can’t use this field in our website is because the search field in our website is outside the vue instance and I have to set the query with vm.query = 'query' when typing into this field. It’s a legacy site which doesn’t help and my choices are very limited.

Here’s the same example in codepen using Algolia’s test credentials: https://codepen.io/javier_ml/pen/RwNzWRZ

Hi @javier,

The duplicate query is happening because you add the unknown query prop to the ais-instant-search component, which causes it to rerender and do another query.

It’s solved like this:

<div id="instant-search">
  <div class="main container instant-search">
    <ais-instant-search :search-client="algoliaClient" index-name="instant_search">
      <ais-configure :hits-per-page.camel="21" :min-word-sizefor-1-typo.camel="6"></ais-configure>
      <ais-search-box></ais-search-box>
      <input v-model="query" />
      <ais-hits></ais-hits>
  </div>
</div>

I’m not sure what the goal is of your synchronising of the query, but I suggest you can do it like this too:

<div id="instant-search">
  <div class="main container instant-search">
    <ais-instant-search :search-client="algoliaClient" index-name="instant_search">
      <ais-configure :hits-per-page.camel="21" :min-word-sizefor-1-typo.camel="6"></ais-configure>
      <ais-search-box></ais-search-box>
      <ais-search-box>
        <input
          slot-scope="{ currentRefinement, refine }"
          :value="currentRefinement"
          @input="refine($event.currentTarget.value)"
          placeholder="Custom SearchBox"
        />
      </ais-search-box>
      <ais-hits></ais-hits>
  </div>
</div>

I’ve edited your pen to include this option too: https://codepen.io/Haroenv/pen/bGNPaoG

Thanks for your explanation about the query prop that makes sense now. I think I oversimplified my problem to be fair so I’ll try to explain it better.

The reason I’m synchronising the query is to be able to display it in a couple places and show/hide some elements if it’s not empty, for example changing your pen to something like this makes the queries trigger multiple times:

<div id="instant-search">
  <div class="main container instant-search">
    <ais-instant-search :search-client="algoliaClient" index-name="instant_search">
      <ais-configure :hits-per-page.camel="21" :min-word-sizefor-1-typo.camel="6"></ais-configure>
      <ais-search-box>
        <input
          slot-scope="{ currentRefinement, refine }"
          :value="currentRefinement"
          @input="query = $event.currentTarget.value; refine($event.currentTarget.value)"
          placeholder="Custom SearchBox"
        />
      </ais-search-box>
      <p>You're searching for: {{ query }}</p>
      <ais-hits></ais-hits>
  </div>
</div>

https://codepen.io/javier_ml/pen/RwNzWRZ

Is there a way to get the same results without triggering multiple operations? On a similar note, is there a way to trigger the refine() method outside a <ais-search-box> component? That could also solve the same issue for us in a different way.

Thanks.

Ah, I see what’s going on. Indirectly it’s still the same thing. If you set a variable in a Vue component, that causes the component to render again.

There’s a few workarounds possible:

  • use ais-state-results to get the query to show conditionally, without rerendering the whole component
<ais-instant-search>
  <!-- ... -->
  <ais-state-results>
    <p slot-scope="{ state: { query } }"> the query is: {{ query }} </p>
  </ais-state-results>
</ais-instant-search>

(you need to use Vue InstantSearch 2.7.0 for this syntax where you have access to the search state too, it’s not yet documented, but will be soon, currently in review)

  • keep the rerendering scope smaller

You can make a smaller part of the app rerender by having multiple components, where you only change the query in the inner component, without rerendering the ais-instant-search component.

Since IIRC this requires you to use the babel plugin for Vue, that might not be an option for you.

  • prevent the client from searching again for an identical query

The situation which is happening is that both queries happen at the same moment, and thus the response is not yet received. Since it isn’t received, the default behaviour is to search again. We have an option to reuse a duplicate request:

const client = algoliasearch('', '', { _useRequestCache: true });
  • update to the next major version of the client

To solve that same problem, you can also update to version 4 of the client algoliasearch@beta now, which has the request cache enabled by default.

Thank you very much I wasn’t aware that changing a variable would make the whole component to re-render although thinking about it, it does makes sense :sweat_smile:

I ended up using a combination of <ais-state-results> to avoid unnecessary re-renders and also _useRequestCache which worked perfectly.

1 Like