Algolia - Store RefinementList value when close Modal

1.1k views Asked by At

I'm building a filter Modal in algolia. On that filter Modal, I have a standard refinementlist (see below code). When the user hits "Search" on the modal, the refinementlist values are lost (ie not applied to my component), but there is no guide on how to store refinementlist output.

What I'd like is to essentially have my Refinement list values not to clear when I close my modal.

refinementlist.js

import React from 'react';

import { RefinementList } from 'react-instantsearch-dom';

const toRefine = () => {
    return (
        <div>
                <RefinementList 
                    attribute={`tags`} 
                    searchable 
                    limit={5} 
                />
        </div>
    );
};

export default toRefine;

filter.js

import React from 'react';
import toRefine from './toRefine';

const Filters = () => {
    return (

                <div>
                    <toRefine />
                </div>

    );
};

export default Filters;

MainPage.js

import React, { useState } from 'react';
import Hits from './hits';
import algoliasearch from 'algoliasearch/lite';
import { InstantSearch } from 'react-instantsearch-dom';
import Modal from 'react-modal';
import Filters from './filters';

Modal.setAppElement('#root');   
const searchC = algoliasearch($ENV_VARS);

const Page = () => {
    const [ modalIsOpen, setIsOpen ] = useState(false); //Hook for modal

    function openModal() {
        setIsOpen(true);
    }
    function closeModal() {
        setIsOpen(false);
    }

    return (
        <div>
            <InstantSearch
                indexName="index" 
                searchClient={searchC}
            >
                <CustomSearchBox />
                <button onClick={openModal}>Show FILTERS</button>
                <Configure hitsPerPage={20} />
                <Hits />
                <Modal
                    isOpen={modalIsOpen}
                    onRequestClose={closeModal}
                    contentLabel="filterElement"
                    className={styles.modal}
                    overlayClassName={styles.overlay}
                >
                    <FilterPage />
                </Modal>
            </InstantSearch>
        </div>
    );
};

export default Page;
1

There are 1 answers

0
Sarah Dayan On

Each React InstantSearch widget is responsible for its UI state and needs to be mounted. I'm not familiar with react-modal, but from what I gather reading their documentation, the modal instance is destroyed when closing, not hidden, so the RefinementList is unmounted as well.

What you can do to circumvent this behavior is persist the widget's state manually whenever it changes except when closing the modal, and inject it to the widget as its default refinement.

function App() {
  const [brandState, setBrandState] = React.useState([]);

  // ...

  return (
    <InstantSearch
      onSearchStateChange={(state) => {
        if (modalIsOpen && state.refinementList?.brand) {
          setBrandState(state.refinementList.brand);
        }
      }}
    >
      <Modal isOpen={modalIsOpen}>
        <RefinementList
          defaultRefinement={brandState}
          attribute="brand"
        />
      </Modal>
    </InstantSearch>
  );
}

You always need to have the RefinementList mounted in the application so that the state is persisted in React InstantSearch's internal state. You can do it declaratively by creating a virtual refinement list, which doesn't render anything, using the connectRefinementList connector.

import { connectRefinementList } from 'react-instantsearch-dom';

// ...

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

function App() {
  // ...

  return (
    <InstantSearch
      onSearchStateChange={(state) => {
        if (modalIsOpen && state.refinementList?.brand) {
          setBrandState(state.refinementList.brand);
        }
      }}
    >
      <VirtualRefinementList defaultRefinement={brandState} attribute="brand" />
      {/* ... */}
    </InstantSearch>
  );
}

You can see it in action in this CodeSandbox demo.