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

"insight:true" shows error in createAutocomplete with NextJS React18 #1218

Closed
Hamlet626 opened this issue Nov 3, 2023 · 2 comments · Fixed by #1220
Closed

"insight:true" shows error in createAutocomplete with NextJS React18 #1218

Hamlet626 opened this issue Nov 3, 2023 · 2 comments · Fixed by #1220

Comments

@Hamlet626
Copy link

Hamlet626 commented Nov 3, 2023

Description

It shows the following error when making “insights:true” in createAutocomplete:

 ⨯ node_modules/@algolia/autocomplete-plugin-algolia-insights/dist/umd/index.production.js (2:4254) @ h
 ⨯ TypeError: Cannot read properties of undefined (reading ‘version’)
    at eval (./src/app/test/autocomplete.tsx:29:161)
    at Autocomplete (./src/app/test/autocomplete.tsx:29:71)

my package.json

{
  “name”: “tester”,
  “version”: “0.1.0”,
  “private”: true,
  “scripts”: {
    “dev”: “next dev”,
    “build”: “next build”,
    “start”: “next start”,
    “lint”: “next lint”
  },
  “dependencies”: {
    “@algolia/autocomplete-core”: “^1.12.1",
    “@algolia/autocomplete-preset-algolia”: “^1.12.1",
    “next”: “14.0.1",
    “react”: “^18",
    “react-dom”: “^18",
    “react-instantsearch-nextjs”: “^0.1.4"
  },
  “devDependencies”: {
    “@types/node”: “^20”,
    “@types/react”: “^18”,
    “@types/react-dom”: “^18”,
    “autoprefixer”: “^10.0.1”,
    “eslint”: “^8”,
    “eslint-config-next”: “14.0.1”,
    “postcss”: “^8”,
    “tailwindcss”: “^3.3.0”,
    “typescript”: “^5”
  }
}

autocomplete.tsx

import {
    AutocompleteOptions,
    AutocompleteState,
    createAutocomplete,
  } from ‘@algolia/autocomplete-core’;
  import { getAlgoliaResults } from ‘@algolia/autocomplete-preset-algolia’;
  import { Hit } from ‘@algolia/client-search’;
  import algoliasearch from ‘algoliasearch/lite’;
  import React from ‘react’;


  const searchClient = algoliasearch(
    ‘latency’,
    ‘6be0576ff61c053d5f9a3225e2a90f76’
  );

  type AutocompleteItem = Hit<{
    brand: string;
    categories: string[];
    image: string;
    name: string;
    objectID: string;
    url: string;
  }>;

  export function Autocomplete(
    props: Partial<AutocompleteOptions<AutocompleteItem>>
  ) {
    const [autocompleteState, setAutocompleteState] = React.useState<
      AutocompleteState<AutocompleteItem>
    >({
      collections: [],
      completion: null,
      context: {},
      isOpen: false,
      query: ‘’,
      activeItemId: null,
      status: ‘idle’,
    });
    const autocomplete = React.useMemo(
      () =>
        createAutocomplete<
          AutocompleteItem,
          React.BaseSyntheticEvent,
          React.MouseEvent,
          React.KeyboardEvent
        >({
          onStateChange({ state }) {
            setAutocompleteState(state);
          },
          insights:true,
          getSources() {
            return [
              {
                sourceId: ‘products’,
                getItems({ query }) {
                  return getAlgoliaResults({
                    searchClient,
                    queries: [
                      {
                        indexName: ‘instant_search’,
                        query,
                        params: {
                          hitsPerPage: 5,
                        },
                      },
                    ],
                  });
                },
                getItemUrl({ item }) {
                  return item.url;
                },
              },
            ];
          },
          ...props,
        }),
      [props]
    );
    const inputRef = React.useRef<HTMLInputElement>(null);
    const formRef = React.useRef<HTMLFormElement>(null);
    const panelRef = React.useRef<HTMLDivElement>(null);
    const { getEnvironmentProps } = autocomplete;

    React.useEffect(() => {
      if (!formRef.current || !panelRef.current || !inputRef.current) {
        return undefined;
      }

      const { onTouchStart, onTouchMove, onMouseDown } = getEnvironmentProps({
        formElement: formRef.current,
        inputElement: inputRef.current,
        panelElement: panelRef.current,
      });

      window.addEventListener(‘mousedown’, onMouseDown);
      window.addEventListener(‘touchstart’, onTouchStart);
      window.addEventListener(‘touchmove’, onTouchMove);

      return () => {
        window.removeEventListener(‘mousedown’, onMouseDown);
        window.removeEventListener(‘touchstart’, onTouchStart);
        window.removeEventListener(‘touchmove’, onTouchMove);
      };
    }, [getEnvironmentProps, autocompleteState.isOpen]);

    return (
      <div className=“aa-Autocomplete” {...autocomplete.getRootProps({})}>
        <form
          ref={formRef}
          className=“aa-Form”
          {...autocomplete.getFormProps({ inputElement: inputRef.current })}
        >
          <div className=“aa-InputWrapperPrefix”>
            <label className=“aa-Label” {...autocomplete.getLabelProps({})}>
            </label>
          </div>
          <div className=“aa-InputWrapper”>
            <input
              className=“aa-Input”
              ref={inputRef}
              {...autocomplete.getInputProps({ inputElement: inputRef.current })}
            />
          </div>
          <div className=“aa-InputWrapperSuffix”>
          </div>
        </form>

        {autocompleteState.isOpen && (
          <div
            ref={panelRef}
            className={[
              ‘aa-Panel’,
              ‘aa-Panel--desktop’,
              autocompleteState.status === ‘stalled’ && ‘aa-Panel--stalled’,
            ]
              .filter(Boolean)
              .join(' ‘)}
            {...autocomplete.getPanelProps({})}
          >
            <div className=“aa-PanelLayout aa-Panel--scrollable”>
              {autocompleteState.collections.map((collection, index) => {
                const { source, items } = collection;

                return (
                  <section key={`source-${index}`} className=“aa-Source”>
                    {items.length > 0 && (
                      <ul className=“aa-List” {...autocomplete.getListProps()}>
                        {items.map((item) => {
                          return (
                            <li
                              key={item.objectID}
                              className=“aa-Item”
                              {...autocomplete.getItemProps({ item, source })}
                            >
                              <div className=“aa-ItemWrapper”>
                                <div className=“aa-ItemContent”>
                                  <div className=“aa-ItemIcon aa-ItemIcon--picture aa-ItemIcon--alignTop”>
                                    <img
                                      src={item.image}
                                      alt={item.name}
                                      width=“40"
                                      height=“40”
                                    />
                                  </div>
                                  <div className=“aa-ItemContentBody”>
                                    <div className=“aa-ItemContentDescription”>
                                      By <strong>{item.brand}</strong> in{’ ’}
                                      <strong>{item.categories[0]}</strong>
                                    </div>
                                  </div>
                                </div>
                                <div className=“aa-ItemActions”>
                                  <button
                                    className=“aa-ItemActionButton aa-DesktopOnly aa-ActiveOnly”
                                    type=“button”
                                    title=“Select”
                                    style={{ pointerEvents: ‘none’ }}
                                  >
                                    <svg fill=“currentColor” viewBox=“0 0 24 24”>
                                      <path d=“M18.984 6.984h2.016v6h-15.188l3.609 3.609-1.406 1.406-6-6 6-6 1.406 1.406-3.609 3.609h13.172v-4.031z” />
                                    </svg>
                                  </button>
                                </div>
                              </div>
                            </li>
                          );
                        })}
                      </ul>
                    )}
                  </section>
                );
              })}
            </div>
          </div>
        )}
      </div>
    );
  }

Reproduction

I just built a test project for integrating autocomplete with nextJS, like this:

Demo →

Run the Repository →:
npm run dev
go to localhost:3000/test

@dhayab
Copy link
Member

dhayab commented Nov 17, 2023

Hi, thanks for your report.

I can confirm there seems to be an issue with insights when enabled during server-side rendering environment.
While we work on fixing this problem in the library, you could try a workaround by enabling insights in browser environment only:

const isBrowser = typeof window !== 'undefined';

createAutocomplete({
  /* ... */,
  insights: isBrowser,
});

@Hamlet626
Copy link
Author

Hi, thanks for your report.

I can confirm there seems to be an issue with insights when enabled during server-side rendering environment. While we work on fixing this problem in the library, you could try a workaround by enabling insights in browser environment only:

const isBrowser = typeof window !== 'undefined';

createAutocomplete({
  /* ... */,
  insights: isBrowser,
});

It works for me, thanks very much for the solution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants