-
Notifications
You must be signed in to change notification settings - Fork 157
Nuxt Instant Search broken when used with router and index mapping #1066
Comments
same here ;( |
@EduardoMateos did you find any solution? |
Hello, I fixed it temporarily, it is not the best solution but this works for me. I get the parameters from the url manually to initialize Algolia. im use Nuxt 2.15.8, IMPORTANT: my index name is "product" PD:The code can be improved, I did it quickly because I don't have much time. But it works. Code: import {
AisInstantSearchSsr,
// AisRefinementList,
AisInfiniteHits,
AisHighlight,
AisSearchBox,
AisStats,
AisPagination,
AisSnippet,
AisStateResults,
AisPoweredBy,
createServerRootMixin,
} from "vue-instantsearch";
import algoliasearch from "algoliasearch/lite";
import _renderToString from "vue-server-renderer/basic";
import { createInfiniteHitsSessionStorageCache } from "instantsearch.js/es/lib/infiniteHitsCache";
function renderToString(app) {
return new Promise((resolve, reject) => {
_renderToString(app, (err, res) => {
if (err) reject(err);
resolve(res);
});
});
}
const indexName = "product";
const searchClient = algoliasearch(
"XXXXX",
"XXXXX"
);
// read and write router state to support algolia query parameter
function nuxtRouter(vueRouter) {
return {
read() {
return vueRouter.currentRoute.query;
},
write(routeState) {
// Only push a new entry if the URL changed (avoid duplicated entries in the history)
if (this.createURL(routeState) === this.createURL(this.read())) {
return;
}
vueRouter.push({
query: routeState,
});
},
createURL(routeState) {
const url = vueRouter.resolve({
query: routeState,
}).href;
return url;
},
onUpdate(cb) {
if (typeof window === "undefined") return;
this._onPopState = (event) => {
const routeState = event.state;
// On initial load, the state is read from the URL without
// update. Therefore, the state object isn't present. In this
// case, we fallback and read the URL.
if (!routeState) {
cb(this.read());
} else {
cb(routeState);
}
};
window.addEventListener("popstate", this._onPopState);
},
dispose() {
if (typeof window === "undefined") return;
window.removeEventListener("popstate", this._onPopState);
},
};
}
export default {
serverPrefetch() {
return this.instantsearch
.findResultsState({
component: this,
renderToString,
})
.then((algoliaState) => {
this.$ssrContext.nuxt.algoliaState = algoliaState;
});
},
beforeMount() {
const results =
(this.$nuxt.context && this.$nuxt.context.nuxtState.algoliaState) ||
window.__NUXT__.algoliaState;
this.instantsearch.hydrate(results);
// Remove the SSR state so it can't be applied again by mistake
delete this.$nuxt.context.nuxtState.algoliaState;
delete window.__NUXT__.algoliaState;
},
data() {
//FIX SSR - GET PARAMETERS FROM URL TO INITIALIZE ALGOLIA
var initialUiState = {};
initialUiState.product = {};
initialUiState.product.refinementList = {};
//QUERY search
if (
this.$router.currentRoute.query.product &&
this.$router.currentRoute.query.product.query
) {
initialUiState.product.query =
this.$router.currentRoute.query.product.query;
}
//filters
if (
this.$router.currentRoute.query.product &&
this.$router.currentRoute.query.product.refinementList &&
this.$router.currentRoute.query.product.refinementList.attr_sexo
) {
initialUiState.product.refinementList.attr_sexo =
this.$router.currentRoute.query.product.refinementList.attr_sexo;
}
if (
this.$router.currentRoute.query.product &&
this.$router.currentRoute.query.product.refinementList &&
this.$router.currentRoute.query.product.refinementList.attr_marca
) {
initialUiState.product.refinementList.attr_marca =
this.$router.currentRoute.query.product.refinementList.attr_marca;
}
if (
this.$router.currentRoute.query.product &&
this.$router.currentRoute.query.product.refinementList &&
this.$router.currentRoute.query.product.refinementList.attr_silo
) {
initialUiState.product.refinementList.attr_silo =
this.$router.currentRoute.query.product.refinementList.attr_silo;
}
const mixin = createServerRootMixin({
searchClient,
indexName,
routing: {
router: nuxtRouter(this.$router),
},
initialUiState: initialUiState,
});
return {
...mixin.data(),
cache: createInfiniteHitsSessionStorageCache(),
};
},
provide() {
return {
$_ais_ssrInstantSearchInstance: this.instantsearch,
};
},
components: {
AisInstantSearchSsr,
AisInfiniteHits,
AisHighlight,
AisSearchBox,
AisStats,
AisPagination,
AisSnippet,
AisStateResults,
AisPoweredBy,
},
}; |
What I'm seeing so far is that the This was introduced in version 4.29.1 of InstantSearch.js, so forcing the dependency of InstantSearch.js to 4.29.0 will avoid this issue (algolia/instantsearch#4849 is what causes it exactly, still trying to figure out how though). You can do that by using resolutions in package.json if you're using Yarn: {
"resolutions": { "instantsearch.js": "4.29.0" }
} thanks for reporting! |
@Haroenv .. nah that does not fix it for me.. instead i get now:
|
That's just a warning @podlebar, unless you're using dynamic widgets? (then you'll need to alias connectDynamicWidgets to EXPERIMENTAL_connectDynamicWidgets` |
yes true.. sorry for that. but the routing behavior is still the same. the route gets overwritten to a empty one.. but i feel like it's happening later than before. but in my updated sandbox it seems to work fine now.. and no i don't use the dynamic widget at all. |
i just tried a hack to now write any rout in the router() function unless the initial query wasn't empty and the user has not interacted with the widgets.. this works fine.. the url keeps staying in the url bar but the serchresults are the initial ones.. so there might be a issue with the indexMapping perhaps. |
I'm guessing the resolutions didn't actually get applied if you're describing that behaviour @podlebar |
well.. i checked my node_modules folder and it was set correctly to 4.29.0. i will do some more testing tomorrow |
@Haroenv seems that "instantsearch.js": "4.19.0" is the last version that reads the url correctly but still writes a empty one after load.. but at least it keeps the results and there is no full reset. |
hmm, are you sure you need to go that low? 4.20.0 doesn't introduce any changes related to routing @podlebar. With the full reset, what do you mean exactly? is it possible you have array/function props in the template itself instead of in data? |
well with "full reset" i mean that i set a filter.. url changes.. results change. Than i reload and the results are correct but the url gets cleared. i'm back on 4.29.0 now as the other version did not have the correct renderState on initial load. |
@EduardoMateos there is a much simpler way:
if you lock the instantsearch.js dep to 4.29.0 like @Haroenv mentioned.. it works really smooth. the 300 milliseconds are somehow a magic number :) |
Thank you!!! I will apply your solution. |
We've filed this internally and will schedule a fix, thanks for reporting! We'll follow up here when we tackle it. |
@sarahdayan any update on this or sort of a timeline? |
This isn't shcheduled yet, but we'll let you know here as soon as it is. |
fixes algolia/vue-instantsearch#1066 fixes algolia/vue-instantsearch#1062 see: #4849 In Vue InstantSearch SSR, the mainIndex gets init'ed *before* it does in regular InstantSearch (see https://codesandbox.io/s/beautiful-mestorf-ijc75?file), and at that point the ui state should already be initialized (before start).
We have just finished a comprehensive refresh of the server-side interaction with routing in the case of Vue InstantSearch. This managed to close this issue for both v3 in 3.9.0 and v4 in 4.3.2. The pull requests in question are:
If the component at the root is the one doing findResultsState, it doesn't have access to $vnode, and in that branch the component name is required to be passed via the first argument.
This is a little more involved, but fixes the issue where routing is not taken in account. Essentially in InstantSearch it was changed so that routing only gets read on .start, but Vue InstantSearch was faking start. Luckily, in a recent version of InstantSearch, some functionality of server rendering / initial results has moved inside InstantSearch itself, and it avoids the issue, as we can use regular 'start' from then on. |
@Haroenv Hi.. and first off all thank you for take care of the issue. |
i just tried it out and it is still not fixed.. event my workarounds stop working as it seems that the method |
short Update: yes the url gets now read on SSR and the results are correct.. but the UI is wrong.. it does not reflect the url |
The setUiState method wasn't removed, could you explain what you mean @podlebar? I'd love to follow up to find what's causing any wrong behaviour! |
hard to say what causes this behavior..
to make the url map with the UI state. You are right.. the method still exists but somehow i could not track it down anymore. |
@podlebar, if you could create a repo that shows this problem clearly, I'll look into it today. In testing this PR (and in the tests), a refinementList is used, as well as multi-index, so in theory what you need that patch for is already handled. Thanks for your cooperation! |
hey.. will try to do one and track the problems down to be more specific.. but i guess it won't be today |
Thanks @podlebar, please open a new issue with it, as it's likely a different thing going on than what caused this issue initially |
Bug 🐞
What is the current behavior?
When used with Nuxt combined with routing and singleIndexMapping the query parameters are removed after hitting refresh.
when this is added on line 106 in the sandbox:
the route on click on a facet does not change at all.
Make a sandbox with the current behavior
https://codesandbox.io/s/friendly-parm-hy2m2?file=/pages/search.vue
What is the expected behavior?
Does this happen only in specific situations?
Happens all the time
What is the proposed solution?
What is the version you are using?
algoliasarch 4.10.5
nuxt 2.15.8
vue-instantsearch 4.0.1
vue-server-renderer 2.6.11
The text was updated successfully, but these errors were encountered: