Search with Algolia in React Native using multiple instance search component

Algolia Jun 27, 2020

In previous blog Search with algolia we experienced algolia search features highlight, search box, filter, etc.

Note: In that blog, we discussed we don’t have that much space available on our mobile screen so we have to extract this RefinementList and display it inside a Modal or another page. So, here we learn how to use more than one InstantSearch component and synchronize the search state between them

So here I'm explaining to you with our previous repository AlogoliaSearchDemo with adding more feature of algolia react native instance search.

In a previous blog, we see to apply the filter on a list of data, We created RefinementList.js file and use that component in our main InstantSearch component tag like in App.js file:

import RefinementList from './src/RefinementList';

// [...]

<InstantSearch
  appId="B1G2GM9NG0"
  apiKey="aadef574be1f9252bb48d4ea09b5cfe5
  indexName="demo_ecommerce"
  root={this.root}
>
  <SearchBox />
  <RefinementList attribute="brand" limit={5} />
  <InfiniteHits />
</InstantSearch>

Create Modal

Here we have space issue for showing list of data with the filter list to apply on that data on one screen. So that's why here needs to create modal for select filter and after select filter modal will be closed and applied filter on a list of data. we’re going to extract this RefinementList and display it inside a Modal.

When using the Modal, components that are displayed inside it are mounted and unmounted depending on whether the Modal is visible or not. One thing to know about React InstantSearch is that a refinement is applied only if its corresponding widget is mounted. If a widget is unmounted then its state is removed from the search state as well.

To keep track of the refinements applied inside the Modal (or another screen if you have navigation), you’ll need to use another InstantSearch component and synchronize the search state between them.

InstantSearch takes two interesting props:

  • onSearchStateChange: a function that is called every time the search state changes.
  • searchState: the search state to apply.

We are going to leverage those two props to keep in sync our two InstantSearch components.

NOTE: To create a modal and show filters on that modal applied that selected filter on list of data, for that we have to, move the declaration of
<RefinementList attribute="brand"> into modal component within parent <IntstantSearch>and import modal into App.js within main <IntstantSearch>.

Filters.js (As a modal)

import React from 'react';
import {
  StyleSheet,
  SafeAreaView,
  Modal,
  Text,
  TouchableOpacity,
} from 'react-native';
import PropTypes from 'prop-types';
import { InstantSearch } from 'react-instantsearch-native';
import RefinementList from './RefinementList';

const styles = StyleSheet.create({
  closeButton: {
    alignItems: 'center',
    marginTop: 20,
  },
  closeButtonText: {
    fontSize: 18,
  },
});

const Filters = ({
  isModalOpen,
  searchState,  
  toggleModal,
  onSearchStateChange,
}) => {
  return <Modal animationType="slide" visible={isModalOpen}>
    <SafeAreaView>
      <InstantSearch
        appId="B1G2GM9NG0"
        apiKey="aadef574be1f9252bb48d4ea09b5cfe5"
        indexName="demo_ecommerce"
        searchState={searchState}
        onSearchStateChange={onSearchStateChange}
      >
        <RefinementList attribute="brand" />
        <TouchableOpacity style={styles.closeButton} onPress={toggleModal}>
          <Text style={styles.closeButtonText}>Close</Text>
        </TouchableOpacity>
      </InstantSearch>
    </SafeAreaView>
  </Modal>
};

Filters.propTypes = {
  isModalOpen: PropTypes.bool.isRequired,
  searchState: PropTypes.object.isRequired,  
  toggleModal: PropTypes.func.isRequired,
  onSearchStateChange: PropTypes.func.isRequired,
};

export default Filters;

App.js (with filter modal)

import Filters from './src/Filters';

// [...]

<InstantSearch
  appId="B1G2GM9NG0"
  apiKey="aadef574be1f9252bb48d4ea09b5cfe5"
  indexName="demo_ecommerce"
  root={this.root}
  searchState={searchState}
  onSearchStateChange={this.onSearchStateChange}
>
  <Filters
    isModalOpen={isModalOpen}              
    searchState={searchState}
    toggleModal={this.toggleModal}
    onSearchStateChange={this.onSearchStateChange}
 /> 
 <SearchBox />
 <Button
   title="Show Filters Modal"
   color="red"              
   onPress={this.toggleModal}
 />         
 <InfiniteHits />
</InstantSearch>

Simulator-Screen-Shot---iPhone-11-Pro-Max---2020-06-27-at-15.13.33Simulator-Screen-Shot---iPhone-11-Pro-Max---2020-06-27-at-14.42.08-8

If you tried the application and selected a brand, you saw that when you closed the Modal, the refinement is not applied to the search. This is because for a refinement to be applied, it needs to be present inside the search state and have a corresponding widget mounted.

So indeed, we will need a RefinementList mounted on our first InstantSearch. Of course, we don’t want to render anything, we just want to apply the refinement.

Luckily we can leverage a concept that is called Virtual Widgets. Those widgets allow you to pre-refine any widget without rendering anything.

Let’s create one for our RefinementList in App.js:

import { InstantSearch, connectRefinementList } from 'react-instantsearch-native';

// [...]

const VirtualRefinementList = connectRefinementList(() => null);

Let’s add it to our App:

// [...]

render() {
    const { isModalOpen, searchState } = this.state;    
    return (
      <SafeAreaView style={styles.safe}>
        <StatusBar barStyle="light-content" />
        <View style={styles.container}>
          <InstantSearch
            appId="B1G2GM9NG0"
            apiKey="aadef574be1f9252bb48d4ea09b5cfe5"
            indexName="demo_ecommerce"
            root={this.root}
            searchState={searchState}
            onSearchStateChange={this.onSearchStateChange}
          >
            <VirtualRefinementList attribute="brand" />
            <Filters
              isModalOpen={isModalOpen}              
              searchState={searchState}
              toggleModal={this.toggleModal}
              onSearchStateChange={this.onSearchStateChange}
            /> 
            <SearchBox />
            <Button
              title="Show Filters Modal"
              color="red"              
              onPress={this.toggleModal}
            />            
            <InfiniteHits />
          </InstantSearch>
        </View>
      </SafeAreaView>
    );
  }

Congrats, you can now filter your results by brands!

That’s it!

You just learned how to create your first React Native InstantSearch application. You can find the source code of this example on GitHub.

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.