Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instantsearch hooks: router not getting along with Next.JS #5241

Closed
dsbrianwebster opened this issue Jun 7, 2022 · 87 comments
Closed

Instantsearch hooks: router not getting along with Next.JS #5241

dsbrianwebster opened this issue Jun 7, 2022 · 87 comments
Labels
Library: React InstantSearch ≥ 7 Issues in any of the react-instantsearch@7 packages (formerly named react-instantsearch-hooks)

Comments

@dsbrianwebster
Copy link

dsbrianwebster commented Jun 7, 2022

We are trying out a server rendered search implementation with NextJS. All seems okay until we try to add a routing object to InstantSearch. We are using routing to have urls for our search state, and to make them SEO friendly (q=potato&type=tuber). There are all sorts of quirks though, from additional re-renders on load to the rest of the application routing breaking.

1. Re-Render Example:
If we try starting at url /search?q=potato we immediately see a re-render to /search.

2. Routing Example:
When we click on a hit/result <Link href={/post/${hit.slug}>🥔 hit</Link> we are taken to our expected /post/${hit.slug} url, but then from there our routing in general seems to be broken. Clicking back moves to /search?q=potato, but only the url changes. Page content is not updated.

StepBrothers_3lg copy

@dsbrianwebster dsbrianwebster changed the title Instantsearch router not getting along with Next.JS Instantsearch hooks: router not getting along with Next.JS Jun 7, 2022
@Haroenv
Copy link
Contributor

Haroenv commented Jun 7, 2022

Just to double check, is that with using the getLocation call, like in the hooks-next example? https://codesandbox.io/s/github/algolia/react-instantsearch/tree/master/examples/hooks-next

It's definitely not a simple problem keeping the state and routers in sync, so if there's a specific edge case missed I'd love to fix that

@francoischalifour
Copy link
Member

francoischalifour commented Jun 7, 2022

Hey @dsbrianwebster – this is likely because Next.js is using Strict Mode, which isn't yet supported in the library with React 18.

Our <InstantSearch> component reads the URL query params to set the initial state, and write the browser URL on unmount to reset it. This is a first-class behavior of the underlying InstantSearch.js library. The problem is that during the second Strict Mode render, the URL has been reset, and therefore the second render is unable to recover the initial state. This breaks URL synchronization.

We're working on React 18 Strict Mode support this week so you can expect a fix in the coming days.

In the mean time, could you disable Strict Mode on the <InstantSearch> component?

Edit: Strict Mode support was introduced in v6.28.0.

@dsbrianwebster
Copy link
Author

Hi @francoischalifour. Thanks for the response! Yeah we have been seeing a bunch of "additional render' issues with other packages after upgrading to React 18 and pretty much roll with strictMode off by default on all projects for the time being.

I can confirm our next config is...

Screen Shot 2022-06-07 at 2 07 01 PM

and we still experience these issues.

@dsbrianwebster
Copy link
Author

dsbrianwebster commented Jun 7, 2022

Hi @Haroenv thanks for the response. And yes, no doubt, def not a simple problem keeping the state and router in sync. Its feels so close 🏁 ! I've forked your sandbox to demonstrate item 2 (Routing example above)...

https://codesandbox.io/s/hooks-next-bug-3506-item-2-6ekug6?file=/pages/index.tsx

To reproduce...

  1. perform a search. either a query or refinement -- anything that triggers a URL change.
  2. Then click on one of the results where you will be routed to '/test'
  3. The click back, the url will change but you will be stuck on the test page

Perhaps issue is something to do with how the InstantSearch component / router is unmounted.

@dsbrianwebster
Copy link
Author

dsbrianwebster commented Jun 7, 2022

@francoischalifour + @Haroenv we've dug a little deeper on Item 1 (The Re-Render Example) and seem to have isolated the issue we're experience there as having something to do with our effort to implement an SEO-friendly URL structure. We're still investigating and will either circle back with an answer there or a sandbox example.

The sandbox provided in the previous comment is still very relevant for @Haroenv as it pertains to item algolia/react-instantsearch#2 edge case.

@joshgeller
Copy link

joshgeller commented Jun 8, 2022

I'm also experiencing the second issue (broken back button).

  1. Refine hits via <RefinementList>
  2. <InstantSearch> appends stringified refinements to the URL
  3. Click a <Link> to go to hit content page
  4. Click back, the URL changes but the page content does not

Possible clue for narrowing down the issue:

  1. Refine hits with <RefinementList>
  2. <InstantSearch> appends stringified refinements to the URL
  3. Refresh the page (so that state is initialized from URL)
  4. Click <Link> to go to hit content page
  5. Click back and everything works correctly

@klaasman
Copy link

klaasman commented Jun 8, 2022

I'm running into issue no. 2 here as well, the broken back button. I think it's caused by the instantsearch.js history implementation performing it's own history mutations, over here:

https://github.com/algolia/instantsearch.js/blob/0a517609de103eef4f8edfefe6e28a1d79a14209/src/lib/routers/history.ts#L142

e.g., try the following on any app rendered by next.js (in this example I'll use https://nextjs.org/)

  1. visit https://nextjs.org/
  2. open dev tools console
  3. run command history.pushState({}, '', '/test-foo-bar') and see the url get updated to the new pathname.
    (this mimics an updated url due to filtering)
  4. click the nav item "Showcase"
    (this mimics clicking a search result)
  5. hit the back button
  6. the pathname goes from /showcase to /test-foo-bar, in the UI nothing happens, you'll stay on the "showcase" page.

The algolia hook history API needs something like custom routing method in order to fix this.

e.g. calling context:

import { InstantSearch } from "react-instantsearch-hooks-web";
import { history } from 'instantsearch.js/es/lib/routers';
import { useRouter } from "next/router";

const router = useRouter();

<InstantSearch
  routing={{
    router: history({
      // please note that `onUpdate` is not an existing part of the api
      onUpdate: (url) => router.push(url),
    })
  }}
/>

When an onUpdate is provided the history instance should obviously no more do it's own history mutations.

This also alllows us to perform a history.replace() instead of history.push() which makes more sense in some filtering contexts.

Edit: the above is already possible with a custom history handler as suggested below.

@Haroenv
Copy link
Contributor

Haroenv commented Jun 8, 2022

You can provide a custom router, you don't need to use history, the methods are described here https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/#widget-param-routing

I'd advise looking at the implementation of the history router, because there's some edge cases we already cover there as a base

@joshgeller
Copy link

joshgeller commented Jun 8, 2022

import { InstantSearch } from "react-instantsearch-hooks-web";
import { history } from 'instantsearch.js/es/lib/routers';
import { useRouter } from "next/router";

const router = useRouter();

<InstantSearch
  routing={{
    router: history({
      onUpdate: (url) => router.push(url),
    })
  }}
/>

Edit: the above is already possible with a custom history handler as suggested below.

@klaasman Do you actually have this working? Because it doesn't look like the history router accepts an onUpdate function. onUpdate is used internally, but I don't see a way to pass a custom function. Same goes for several other options mentioned in @Haroenv's link above.

It would be nice if history allowed the user to pass custom functions for onUpdate, read, write, etc. I'd rather not fork and maintain this router if I can avoid it.

Please let me know if I'm misunderstanding any of this.

@klaasman
Copy link

klaasman commented Jun 9, 2022

@joshgeller Sorry for the lack of clarity, what I meant is that it would be nice to make that onUpdate part of the api.

FYI: I've copied the original history.ts into my repo and made some adjustments in order to use the nextjs router instead - it certainly hasn't been cleaned up but feel free to take a look here: https://gist.github.com/klaasman/b0e06e8f63dbae70829424232285570c

@DB-Alex
Copy link

DB-Alex commented Jun 18, 2022

Hey @dsbrianwebster – this is likely because Next.js is using Strict Mode, which isn't yet supported in the library with React 18.

Our <InstantSearch> component reads the URL query params to set the initial state, and write the browser URL on unmount to reset it. This is a first-class behavior of the underlying InstantSearch.js library. The problem is that during the second Strict Mode render, the URL has been reset, and therefore the second render is unable to recover the initial state. This breaks URL synchronization.

We're working on React 18 Strict Mode support this week so you can expect a fix in the coming days.

In the mean time, could you disable Strict Mode on the <InstantSearch> component?

Can you maybe tell us the status of the Strict Mode support and good routing in next?

@francoischalifour
Copy link
Member

@DB-Alex Strict mode support was released in v6.28.0.

Do you have other routing issues than the ones mentioned in this thread?

@saybou
Copy link

saybou commented Jun 20, 2022

Hi @francoischalifour , i'm still experiencing the 2 issues mentioned by @dsbrianwebster (i'm using nextjs 12 & react 17), especially the broken back button

@DB-Alex
Copy link

DB-Alex commented Jun 20, 2022

I also have this warning:

dev: warn  - ../../node_modules/react-instantsearch-hooks-server/dist/es/getServerState.js
dev: Critical dependency: the request of a dependency is an expression

And when I switch pages in Next like this:

  • Homepage -> no algolia
  • Category page -> has algolia
  • To homepage -> no algolia

Its crashing in the instantsearch clean up:

TypeError: Cannot read properties of null (reading 'state')

Call Stack
eval
../../node_modules/instantsearch.js/es/widgets/index/index.js (530:0)
Array.forEach
<anonymous>
Object.dispose
../../node_modules/instantsearch.js/es/widgets/index/index.js (520:0)
eval
../../node_modules/instantsearch.js/es/widgets/index/index.js (254:0)
Array.reduce
<anonymous>
Object.removeWidgets
../../node_modules/instantsearch.js/es/widgets/index/index.js (252:0)
InstantSearch.removeWidgets
../../node_modules/instantsearch.js/es/lib/InstantSearch.js (357:0)
InstantSearch.dispose
../../node_modules/instantsearch.js/es/lib/InstantSearch.js (494:0)
eval
../../node_modules/react-instantsearch-hooks/dist/es/lib/useInstantSearchApi.js (40:0)
safelyCallDestroy
../../node_modules/react-dom/cjs/react-dom.development.js (22932:0)
commitHookEffectListUnmount
../../node_modules/react-dom/cjs/react-dom.development.js (23100:0)
commitPassiveUnmountInsideDeletedTreeOnFiber
../../node_modules/react-dom/cjs/react-dom.development.js (25098:0)
commitPassiveUnmountEffectsInsideOfDeletedTree_begin
../../node_modules/react-dom/cjs/react-dom.development.js (25048:0)
commitPassiveUnmountEffects_begin
../../node_modules/react-dom/cjs/react-dom.development.js (24956:0)
commitPassiveUnmountEffects
../../node_modules/react-dom/cjs/react-dom.development.js (24941:0)
flushPassiveEffectsImpl
../../node_modules/react-dom/cjs/react-dom.development.js (27038:0)
flushPassiveEffects
../../node_modules/react-dom/cjs/react-dom.development.js (26984:0)
eval
../../node_modules/react-dom/cjs/react-dom.development.js (26769:0)
workLoop
../../node_modules/scheduler/cjs/scheduler.development.js (266:0)
flushWork
../../node_modules/scheduler/cjs/scheduler.development.js (239:0)
MessagePort.performWorkUntilDeadline
../../node_modules/scheduler/cjs/scheduler.development.js (533:0)

Here is a snippet of line 530

localWidgets.forEach(function (widget) {
        if (widget.dispose) {
          // The dispose function is always called once the instance is started
          // (it's an effect of `removeWidgets`). The index is initialized and
          // the Helper is available. We don't care about the return value of
          // `dispose` because the index is removed. We can't call `removeWidgets`
          // because we want to keep the widgets on the instance, to allow idempotent
          // operations on `add` & `remove`.
          widget.dispose({
            helper: helper,
            state: helper.state, <----- 530
            parent: _this5
          });
        }
      });

@nico-mautone
Copy link

Hi, I'm also still experiencing issue number 2, mentioned by @dsbrianwebster (i'm using nextjs 12 & react 18). Has anyone come up to a solution? The custom router implementation maybe? Thanks

@RudiPersson
Copy link

@dhayab posted a workaround using Next useRouter and useEffect in a discussion.
Worked for me :)

https://github.com/algolia/react-instantsearch/discussions/3376#discussioncomment-2297218

@nico-mautone
Copy link

Thanks, but I couldn't make it work with that approach as I have a whole application with tons of search state, that is not attached to a single React useState as it is on the sandbox example. Because of that, I wasn't able to apply URL changes to the algolia UiState, because I don't have access to a function such as "setIndexUiState" provided by useInstantSearch, because hooks can't be called outside the InstatSearch Wrapper. (in the sandbox it uses directly useState setter).

@nico-mautone
Copy link

nico-mautone commented Jul 14, 2022

Thanks @dhmacs, I used that workaround for the moment, triggering a page reload once the popstate occurs over the search page. Something simple like this for the moment, until issue is fixed:

useEffect(() => {
    const handleRouteChange = () => {
      if (window.location.href.includes('${searchPage}')) window.location.reload();
    };
    window.addEventListener('popstate', handleRouteChange);
    return () => {
      window.removeEventListener('popstate', handleRouteChange);
    };
  }, []);

@leeran7
Copy link

leeran7 commented Jul 19, 2022

I'm experiencing the same back button routing bug as @dsbrianwebster. I recreated it and you can use this vercel link and this source code to check it out.

@JulesVerdijk
Copy link

I ran into this problem as well and created a, pretty hacky, fix that seems to work (not used in production yet, so fingers crossed). What I did was taking full control of the routing, not using the router provided by Instantsearch.

Requirement for this solution is a helper that can convert an UiState to a URL (like the createURL function in the algolia router) and back again (like the parseURL function).

In basis the solution works like this:

const AlgoliaCollection: React.FC<any> = ({ serverState }) => {
    const nextRouter = useRouter();

    const onStateChange: InstantSearchProps['onStateChange'] = ({ uiState }) => {
        setUiStateFromAlgolia(uiState);
        const urlForUiState = uiStateToUrl(uiState, INDEX_NAME);

        nextRouter.push(urlForUiState, undefined, { shallow: true });

    return (
        <InstantSearchSSRProvider {...serverState}>
            <InstantSearch searchClient={algoliaClient} indexName={INDEX_NAME} onStateChange={onStateChange}>
                <RefinementList />
                <Hits />
            </InstantSearch>
        </InstantSearchSSRProvider>
    );
}

export default AlgoliaCollection;

So you now control the updating of the UiState yourself, and use this to update the NextRouter accordingly. However this is not enough, it works one way only. On onStateChange the route changes, but if you route through Next (e.g. press the back button) the UiState does not update accordingly. To be able to do this you need to be able to setUiState on a routing event, but InstantSearch does not propegate a setUiState function outside of the onStateChange prop (can we please get that Algolia?). However, this function ís available in child-components in the useInstantSearch hook. I abused this to set the UiState based on routing changes, something like this:

const UpdateStateBasedOnRoute: React.FC<{ uiStateFromAlgolia: UiState, indexName: string }> = ({ uiStateFromAlgolia, indexName }) => {
    const { setUiState } = useInstantSearch();
    const nextRouter = useRouter();

    // This fires on every uiStateFromAlgoliaChange
    useEffect(() => {
        // Create a URL based on the uiStateFromAlgolia (which is set in `AlgoliaCollection` based on the onStateChange event
        const urlFromUiState = uiStateToUrl(uiStateFromAlgolia, indexName);

        // Create a URL based on the actual route
        const urlFromRouter = nextRouter.asPath.includes('?') ? `?${nextRouter.asPath.split('?')[1]}` : ``;

        // These URLs should be identical, if not, routing has probably changed and thus the UiState should be updated accordingly
        //
        if (urlFromUiState !== urlFromRouter) {
            const newUiState = urlToUiState(`${process.env.NEXT_PUBLIC_WEB_URL}${nextRouter.asPath}`, indexName);
            setUiState(newUiState);
        } else {
            setUiState(uiStateFromAlgolia);
        }
    }, [uiStateFromAlgolia, nextRouter]);

    return null;
};

// uiStateFromServer is a URL to UiState done on the server, so the `uiStateFromAlgolia` is correct on first load
const AlgoliaCollection: React.FC<any> = ({ serverState, uiStateFromServer }) => {
    const nextRouter = useRouter();

    const [uiStateFromAlgolia, setUiStateFromAlgolia] = useState(uiStateFromServer);

    const onStateChange: InstantSearchProps['onStateChange'] = ({ uiState }) => {
        setUiStateFromAlgolia(uiState);
        const urlForUiState = uiStateToUrl(uiState, INDEX_NAME);

        nextRouter.push(urlForUiState, undefined, { shallow: true }).then(() => {});
    };

    return (
        <InstantSearchSSRProvider {...serverState}>
            <InstantSearch searchClient={algoliaClient} indexName={INDEX_NAME} onStateChange={onStateChange}>
                <UpdateStateBasedOnRoute uiStateFromAlgolia={uiStateFromAlgolia} indexName={INDEX_NAME} />
                <RefinementList />
                <Hits />
            </InstantSearch>
        </InstantSearchSSRProvider>
    );
}

export default AlgoliaCollection;

Note that I pass uiStateFromAlgolia as a prop to UpdateStateBasedOnRoute , but it is also available from useInstantSearch however I found passing it made it update more smoothly (it is a hacky solution!).
With again a disclaimer that this works in a development environment but I haven’t used it in production yet.

This would work a lot more smoothly if we could actually get a setUiState prop directly on InstantSearch so this can all be controlled within the main component instead of in an empty child component.

@tadinski
Copy link

tadinski commented Jul 30, 2022

Experiencing the same routing problem as described by @dsbrianwebster in n.2 issue.

@AryanJ-NYC
Copy link

AryanJ-NYC commented Aug 1, 2022

@JulesVerdijk your solution mostly worked but broke our infinite scrolling.

We've decided to go back to the old instantsearch package which is a shame.

@dhayab, is there an ETA on a fix?

@tadinski
Copy link

tadinski commented Aug 2, 2022

@dhayab posted a workaround using Next useRouter and useEffect in a discussion. Worked for me :)

#3376 (comment)

This workaround works for me.

@dhayab
Copy link
Member

dhayab commented Aug 12, 2022

Hi, here's a sandbox with another workaround that shares some similarities with @JulesVerdijk : https://codesandbox.io/s/rish-next-router-handler-is22ev?file=/pages/%5Bcategory%5D.tsx.

It consists of a useNextRouterHandler() Hook that returns:

  • an initialUiState that needs to be passed to <InstantSearch> (for SSR)
  • a <NextRouterHandler /> empty component that should be mounted in <InstantSearch> (to be able to use Hooks from React InstantSearch Hooks)

It required an explicit definition of the route/state mapping, and supports Next.js dynamic routes (by specifying the route query to inject).

I tested it on some sandbox shared in the issues, and it works well. Feel free to share below if there are edge cases that are not handled by this workaround.

@aymeric-giraudet
Copy link
Member

@LefanTan :
Yes it's a known limitation of getServerState, since it's not rendered by Next.js it does not have access to its router so useRouter returns null. I'll make note of it for us to add disclaimers but to circumvent it you should maybe avoid using useRouter directly and you could maybe wrap it into a custom hook that returns a stub object when useRouter returns null.

@wenche :
Thanks for the reproduction !
I see you have reactStrictMode: true, these bugs indeed do happen on strict mode while in development mode. The component mounts twice, and it seems like the router middleware does not get unsubscribed on first unmount, so the router tries to setUiState on the first instance which is stale.
I will look for a way for InstantSearch to properly recreate the router.

@sbkl
Copy link

sbkl commented Feb 8, 2023

@aymeric-giraudet Works perfectly for my use case and just pushed it to production :)

@wenche
Copy link

wenche commented Feb 8, 2023

@aymeric-giraudet Thank you for your reply. I can confirm that things seems to work fine without reactStrictMode. 👍 However, I really hope that you are able to fix this as we don't see it as a good long term solution to disable this as we want our code base to work properly especially with React 18's new behaviour for useEffect locally 😃 But it's fine for a short amount of time to disable this.

Might also be an idea to highlight this gotcha in the package's README since it's strongly suggested to enable this flag and for example create-next-app enables this by default.

Looking forward to the final release of this package as it solves on of our main issues with using Algolia and Next 🙌

@aymeric-giraudet
Copy link
Member

Hi @wenche, thanks again for the replies !
I totally agree, we'll further iterate on how InstantSearch cleans up after unmounting, as this problem could impact other middlewares as well. It should be good by the time the official package is released :)

@FaroukMekk
Copy link

When using the back button from a page not using InstantSearch to my InstantSearch page, every facet is removed from the url (and from the state). Is it the expected behavior ?

@adesege
Copy link

adesege commented Feb 10, 2023

Doesn't seem to work well with dynamic paths.

@Haroenv
Copy link
Contributor

Haroenv commented Feb 10, 2023

@adesege do you have a reproduction? the example in the repo is using a dynamic path

@adesege
Copy link

adesege commented Feb 12, 2023

@dhayab's fix works for me.

My use case requires using one of the facets as a path param. However, using instantsearch-router-next-experimental did not properly re-render the next page when the facet is selected.

@aymeric-giraudet
Copy link
Member

Hi @adesege, do you mean that when you click on a <Link> from next/link that goes to the same dynamic route (e.g. /search/[category]), InstantSearch is not aware of the URL change ?
Normally it should and that's one of the issues we meant to address, although ideally you should use InstantSearch's refine functions to change state.
Could you please send a link to a reproduction ? It would be very helpful for us to make the best solution. Thanks !

@franciskodama
Copy link

franciskodama commented Feb 14, 2023

@aymeric-giraudet

First of all, thank you, and congratulations on the work here. It's great to know that we can count on people like you here. :)

I know you replied to osseonews that the BACK BUTTON ISSUE was resolved in this package, however, in our project, this problem persists.

  • In our project, we have a product listing page like any e-commerce.
  • If the user goes to page 2 of one of the categories of this list of products (../us/s/women/clothing/dresses?products%5Bpage%5D=2), then clicks on one of the products, and clicks on the browser's back button afterward, he/she goes back to page 1 (../us/s/women/clothing/dresses).

From what I've read here, what's happening in our project is the same as in other people's reports.
"Clicking back moves to /search?q=potato, but only the url changes. Page content is not updated."

  • Afterward, the page reloads causing us to lose the URL with the query and instead return to ../us/s/women/clothing/dresses.

So, I'm curious to know if you're still receiving reports of this Back Button Issue or if you're aware that the problem might still exist.
PS.: Of course, we know that the problem could be on our end.

Also, do you have any predictions as to when the official production use package will be released? We're eagerly waiting for it, and any information you could provide would be greatly appreciated!

Thanks!

@Haroenv
Copy link
Contributor

Haroenv commented Feb 15, 2023

The production package has been released, but not yet announced as we didn't finish the documentation. In the mean time you can use the package already: https://www.npmjs.com/package/react-instantsearch-hooks-router-nextjs

We're not aware of any further issues relating to the back button, so please create a reproduction so we can debug it.

@aymeric-giraudet
Copy link
Member

Hi @franciskodama,

You can also check out this CodeSandbox that shows the router in action : https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react-hooks/next-routing

Even by adding a <Pagination> component and clicking on the next page then on the link in <Hits>, pressing the back button does work.
You can see before going on the article page that if you type window.history.state in the console you have a Next router history state. Which kind of state do you have on your app before navigating ? Do you have a custom <Pagination> component that handles navigation by itself ?

@JugglerX
Copy link

@franciskodama I have the same problem with the back button after updating to "react-instantsearch-hooks-web": "^6.40.0"

So, I'm curious to know if you're still receiving reports of this Back Button Issue or if you're aware that the problem might still exist.

@mikkio-j
Copy link

@franciskodama @JugglerX have similar problem currently with react-instantsearch-hooks-router-nextjs.

  1. Going on listing page and adding a filter. e.g. women?color=black
  2. Navigating to different subcategory e.g /women/jackets/
  3. Hitting back button, url changes, moment later the content changes, and filters are cleared: women?color=black -> /women/

@franciskodama
Copy link

Hey everyone!
@JugglerX, @mikkio-j

Sorry for the delay in getting back to you. We're still trying to figure out what's going on, but it seems like the package that Haroenv mentioned above is working well. We realized that the issue is something on our end and we still don't have a solution for that.

I just wanted to take a moment to thank all of you for your help, @aymeric-giraudet, and @Haroenv. Your assistance has been much appreciated!

@Haroenv
Copy link
Contributor

Haroenv commented Feb 20, 2023

@mikkio-j, @JugglerX, @franciskodama, does any of you have a reproduction of this issue?

@aymeric-giraudet
Copy link
Member

@JugglerX :
Did you also install react-instantsearch-hooks-router-nextjs and created a new router with createInstantSearchRouterNext that you passed to routing ? Just updating react-instantsearch-hooks to the latest version doesn't fix it.

@mikkio-j , @franciskodama :
What happens if you select a refinement and refresh the page with F5/Cmd+R ? Does the URL get cleared too ? Or is it only when going back/forward ? A reproduction would help indeed :)

@DB-Alex
Copy link

DB-Alex commented Feb 20, 2023

package.json:

        "react-instantsearch-hooks-server": "^6.40.0",
        "react-instantsearch-hooks-web": "^6.40.0",
        "react-instantsearch-hooks-router-nextjs": "^6.40.0",

Everything works as expected until we start using sorting. As soon as we use sorting and hit a product the browser back button does not work anymore. Filtering works as expected.

@aymeric-giraudet
Copy link
Member

Hi @DB-Alex,

May you send a reproduction please ?
I tried adding a SortBy in our example and it works as expected : https://codesandbox.io/s/vigorous-shockley-gkvw1r?file=/pages/index.tsx
If you sort by price, click on a hit and then press the back button it does work.

@aymeric-giraudet
Copy link
Member

aymeric-giraudet commented Feb 21, 2023

Hi @mikkio-j,

I've seen a ticket you've raised to our support and checked, it's not due to routing really, it's the way Next.js, React and InstantSearch work.
You can see the issue is not just when pressing the back button but also on reload and when clicking on the links (e.g. from category/women/socks to category/women?color=blue it doesn't work either)

  • To fix for reload it's because you're not passing blocks to your component in getServerState.
  • For the back button and link clicks removing filters, it's because at this point it's doing CSR and keeping the same InstantSearch instance across navigation, and since you're using new keys, your useRefinementList hooks get unmounted and clear the state. A quick way to fix it would be to force remounting InstantSearch by setting key={url} in InstantSearchSSRProvider.

I'll send back a fork of your CodeSandbox with the solution to customer support as it might be private for you :)

P.S : Overall maybe this could be avoided by relying on refine functions from our hooks rather than Next.js Link I think

@aymeric-giraudet
Copy link
Member

aymeric-giraudet commented Feb 21, 2023

Hi everyone ! 🙋‍♂️

First of all, thank you for your patience and for providing precious feedback.

Our solution for creating a Next.js compatible router for InstantSearch is now generally available and published on npm as react-instantsearch-hooks-router-nextjs. It has to be a separate package as it has next as a peerDependency.

Its documentation is available on the Algolia docs right there : https://www.algolia.com/doc/api-reference/widgets/instantsearch-next-router/react-hooks/

You can also see it in action on CodeSandbox : https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react-hooks/next-routing

If you're seeing any problem still, please raise another issue with a reproduction and make sure to check that it's not just related to back button press, try to see if reloading the page raises the same issue. If you're also on a dynamic route, check that the issue is not caused by components/hooks being unmounted.

Thanks again to all of you !

@DB-Alex
Copy link

DB-Alex commented Feb 21, 2023

Hi @DB-Alex,

May you send a reproduction please ? I tried adding a SortBy in our example and it works as expected : https://codesandbox.io/s/vigorous-shockley-gkvw1r?file=/pages/index.tsx If you sort by price, click on a hit and then press the back button it does work.

This was resolved by adding a key to the SSRProvider:

<InstantSearchSSRProvider
	{...serverState}
	key={router.asPath}
>

@JugglerX
Copy link

@aymeric-giraudet Thank you for working so hard on a fix for this issue. I wanted to provide an update on my solution.

The problem for me seemed to be that my <Layout/> component was unmounting between routes.

Here is some more detail as It might help others:

I needed to add the new package react-instantsearch-hooks-router-nextjs and implement it as per the docs. https://www.algolia.com/doc/api-reference/widgets/instantsearch-next-router/react-hooks/

I was able to get a working version with a basic new Nextjs site. But when I installed the above on my existing site the back button issue continued to occur.

Stripping back my Nextjs site, the issue was resolved when I removed the layout component that wrapping each page. I modified my layout architecture to that suggested in the Nextjs docs here https://nextjs.org/docs/basic-features/layouts#per-page-layouts

@DB-Alex
Copy link

DB-Alex commented Feb 22, 2023

@aymeric-giraudet I found the issue with the back button!

Works!:

  • Page with Instant Search
  • Click on a and go to a page without Instant Search everything works as expected
  • Click on back button, works as expected

Breaks back button:

  • Click on a and go to a page with Instant Search
  • Click on a and go to a page with Instant Search (same index in my case, don't know if it makes a difference) everything works as expected
  • Click on back button, page hangs. It will not go back anymore until you click another link somewhere to a page without Instant Search

Even with this key:

<InstantSearchSSRProvider
	{...serverState}
	key={router.asPath}
>

@kblizeck
Copy link

kblizeck commented Feb 28, 2023

@aymeric-giraudet Installed the new package and it works great with refinement, menus, clicking through to a Hit and then using the back button. However, I was previously sending url parameters to pre-set search and Menu and those are being cleared from the URL and not applied to the InstantSearch state. Any suggestions on how to tackle pre-defined url parameters?

Steps to issue:

@Haroenv
Copy link
Contributor

Haroenv commented Feb 28, 2023

@kblizeck, could you create a reproduction and a new issue please?

@aymeric-giraudet
Copy link
Member

Hi everyone ! 🙌

I'm sorry but I will have to lock the issue so that people don't have to scroll up to find the solution.

Our solution for creating a Next.js compatible router for InstantSearch is now generally available and published on npm as react-instantsearch-hooks-router-nextjs. It has to be a separate package as it has next as a peerDependency.

Its documentation is available on the Algolia docs right there : https://www.algolia.com/doc/api-reference/widgets/instantsearch-next-router/react-hooks/

You can also see it in action on CodeSandbox : https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react-hooks/next-routing

If you're seeing any problem still, please raise another issue with a reproduction.

Thanks again to all of you !

@algolia algolia locked as resolved and limited conversation to collaborators Mar 2, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Library: React InstantSearch ≥ 7 Issues in any of the react-instantsearch@7 packages (formerly named react-instantsearch-hooks)
Projects
None yet
Development

No branches or pull requests