Expo / React Native with Typesense InstantSearch Adapter

iPhone Typesense InstantSearch.js

Install Instantsearch according to the guide of Algolia:

npx expo install algoliasearch react-instantsearch-core

Install Typesense according to the guide of Typesense:

npm install --save typesense-instantsearch-adapter @babel/runtime

Set up Typesense in your App.js file or a Expo Router file according to the guide of Typesense for React Native. Some settings need to be changed to get it working.

You need to create an SearchBox and InfiniteHits component:

# ./components/SearchBox.js

import React, { useRef, useState } from 'react';
import { StyleSheet, View, TextInput } from 'react-native';
import { useSearchBox } from 'react-instantsearch-core';

export function SearchBox(props) {
  const { query, refine } = useSearchBox(props);
  const [inputValue, setInputValue] = useState(query);
  const inputRef = useRef(null);

  function setQuery(newQuery) {
    setInputValue(newQuery);
    refine(newQuery);
  }

  // Track when the InstantSearch query changes to synchronize it with
  // the React state.
  // We bypass the state update if the input is focused to avoid concurrent
  // updates when typing.
  if (query !== inputValue && !inputRef.current?.isFocused()) {
    setInputValue(query);
  }

  return (
    <View style={styles.container}>
      <TextInput
        ref={inputRef}
        style={styles.input}i
        value={inputValue}
        onChangeText={setQuery}
        clearButtonMode="while-editing"
        autoCapitalize="none"
        autoCorrect={false}
        spellCheck={false}
        autoComplete="off"
        placeholder='Zoeken ...'
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: '#252b33',
    padding: 18,
  },
  input: {
    height: 48,
    padding: 12,
    fontSize: 16,
    backgroundColor: '#fff',
    borderRadius: 4,
    borderWidth: 1,
    borderColor: '#ddd',
  },
});
# ./components/InfiniteHits.js

import React from 'react';
import { StyleSheet, View, FlatList } from 'react-native';
import { useInfiniteHits } from 'react-instantsearch-core';

export function InfiniteHits({ hitComponent: Hit, ...props }) {
  const { hits, isLastPage, showMore } = useInfiniteHits({
    ...props,
    escapeHTML: false,
  });

  return (
    <FlatList
      data={hits}
      keyExtractor={(item) => item.objectID}
      ItemSeparatorComponent={() => <View style={styles.separator} />}
      onEndReached={() => {
        if (!isLastPage) {
          showMore();
        }
      }}
      renderItem={({ item }) => (
        <View style={styles.item}>
          <Hit hit={item} />
        </View>
      )}
    />
  );
};

const styles = StyleSheet.create({
  separator: {
    borderBottomWidth: 1,
    borderColor: '#ddd',
  },
  item: {
    padding: 18,
  },
});

This will all lead to the App.js file:

# App.js

import { StyleSheet } from 'react-native';

import React from "react";
import { InstantSearch } from 'react-instantsearch-core';
import TypesenseInstantSearchAdapter from "typesense-instantsearch-adapter";
import { SearchBox } from '@/components/SearchBox';
import { InfiniteHits } from '@/components/InifiniteHits';

const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
  server: {
    apiKey: "YOURKEY", // Be sure to use an API key that only allows search operations
    nodes: [
      {
        host: "CLOUDURL.typesense.net",
        port: 443,
        path: "", // Optional. Example: If you have your typesense mounted in localhost:8108/typesense, path should be equal to '/typesense'
        protocol: "https",
      },
    ],
    cacheSearchResultsForSeconds: 2 * 60, // Cache search results from server. Defaults to 2 minutes. Set to 0 to disable caching.
  },
  // The following parameters are directly passed to Typesense's search API endpoint.
  //  So you can pass any parameters supported by the search endpoint below.
  //  query_by is required.
  additionalSearchParameters: {
    query_by: "name",
  },
});
const searchClient = typesenseInstantsearchAdapter.searchClient;

export default function App() {
  return (<InstantSearch indexName="PRODUCTSORYOURINDEX" searchClient={searchClient}>
    <SearchBox />
    <InfiniteHits hitComponent={Hit} />
  </InstantSearch>);
}

function Hit({ hit }) {
    return (
      <Text>{hit.name}</Text>
    );
}

You can configure more fields, like a Filter, etc, by following on the guide from Algolia.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top