algolia / instantsearch

⚡️ Libraries for building performant and instant search and recommend experiences with Algolia. Compatible with JavaScript, TypeScript, React and Vue.
https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/
MIT License
3.69k stars 515 forks source link

DynamicWidgets does not work with Remix Link #5552

Open dziugas-liaudinskas opened 1 year ago

dziugas-liaudinskas commented 1 year ago

🐛 Current behavior

When the component which is passed to the getServerState method contains DynamicWidgets and Link from “@remix-run/react” components it causes the app to break with the error: “useHref() may be used only in the context of a component.”. If DynamicWidgets or Link is removed then everything seems fine

🔍 Steps to reproduce

  1. Go to https://codesandbox.io/p/sandbox/lucid-cerf-glr0tu
  2. Everything should work fine, you can see filters and hits
  3. In app/components/Search.tsx change line 97 from <Hits hitComponent={Hit} /> to <Hits hitComponent={HitWithLink} /> (same component just wrapped with Link)
  4. "Error: useHref() may be used only in the context of a component."
  5. Now delete <DynamicWidgets>, refresh and the app is working again (just without filters)

Live reproduction

https://codesandbox.io/p/sandbox/lucid-cerf-glr0tu

💭 Expected behavior

DynamicWidgets should work with Remix Link

Package version

react-instantsearch-hooks-web 6.41.0, react-instantsearch-hooks-server 6.41.0

Operating system

macOS

Browser

Chrome

Code of Conduct

dhayab commented 1 year ago

Hi, in getServerState() when we detect a dynamic widget we run renderToString on your search component a second time to get the initial state of all the dynamic facets we received from Algolia. This time, apparently the router context is lost, which creates the error you see.

We'll look at if and how we can fix that, but in the meantime, you can conditionally wrap your Hit component with a Link, depending on whether you detect a router context or not. Here's an example:

import { useInRouterContext } from 'react-router';

/* ... */

function HitWithLink({ hit }: HitProps) {
  const inRouterContext = useInRouterContext();
  const Hit = () => (
    <>
      <Highlight hit={hit} attribute="name" className="Hit-label" />
      <span className="Hit-price">${hit.price}</span>
    </>
  );

  if (inRouterContext) {
    return (
      <Link to="/new">
        <Hit />
      </Link>
    );
  }

  return <Hit />;
}