import { useReducer , useCallback , useEffect } from 'react' ; import algoliasearch , { SearchIndex } from 'algoliasearch' ; import { RequestOptions } from '@algolia/transporter' ; import { SearchOptions , SearchResponse } from '@algolia/client-search' ; param appId param searchKey param indexName export const createAlgoliaIndex = ( appId ? , searchKey ? , indexName ? ) => { if ( ! appId || ! searchKey || ! indexName ) return null ; return algoliasearch ( appId , searchKey ) . initIndex ( indexName ) ; } ; /** Current request state, hits retrieved, and loading status. */ interface SearchState < Hit > { /** Algolia SearchResponse object — contains only last page of hits retrieved */ response : SearchResponse < Hit > | null ; /** Contains all hits for search query, including all pages retrieved */ hits : SearchResponse < Hit > [ 'hits' ] ; /** Set when loading initially or loading more hits */ loading : boolean ; /** Flag set if there are more pages to be retrieved */ hasMore : boolean ; /** Algolia App ID */ appId : string ; /** API key to search the index */ searchKey : string ; /** Algolia index to query */ indexName : string ; /** The Algolia search index created */ index : SearchIndex | null ; } param prevState param updates const generateSearchReducer = < Hit > ( ) => ( prevState , updates ) : SearchState < Hit > => { const gotMore = updates ?. response ?. page && updates ?. response ?. page > 0 ; const hits = gotMore && updates . response ? [ ... prevState . hits , ... updates . response . hits ] : updates . hits ?? updates . response ?. hits ?? prevState . hits ?? [ ] ; const hasMore = updates . response ? updates . response . page < updates . response . nbPages - 1 : false ; return { ... prevState , ... updates , hits , hasMore } ; } ; param appId param searchKey param indexName param initialRequest returns export function useAlgolia < Hit = any > ( appId , searchKey , indexName , initialRequest = { } ) { const [ searchState , searchDispatch ] = useReducer ( generateSearchReducer < Hit > ( ) , { response : null , hits : [ ] , loading : false , hasMore : false , appId , searchKey , indexName , index : createAlgoliaIndex ( appId , searchKey , indexName ) , } ) ; const { index } = searchState ; const [ request , requestDispatch ] = useReducer ( ( prev , updates ) => ( { ... prev , ... updates } ) , initialRequest ) ; const query = useCallback ( async ( page ? ) => { if ( ! index ) return ; if ( typeof page === 'number' && page > 0 ) searchDispatch ( { loading : true } ) ; else searchDispatch ( { loading : true , hits : [ ] } ) ; const response = await index . search < Hit > ( '' , { ... request , page : page ?? request . page ?? 0 , } ) ; searchDispatch ( { response , loading : false } ) ; } , [ index , request ] ) ; useEffect ( ( ) => { query ( ) ; } , [ query ] ) ; const getMore = ( ) => { if ( searchState . response && ! searchState . loading && searchState . hasMore ) query ( searchState . response . page + 1 ) ; } ; const setAlgoliaConfig = useCallback ( ( newConfig ) => { const updates = { } ; if ( newConfig . appId ) updates . appId = newConfig . appId ; if ( newConfig . searchKey ) updates . searchKey = newConfig . searchKey ; if ( newConfig . indexName ) updates . indexName = newConfig . indexName ; updates . index = createAlgoliaIndex ( updates . appId ?? searchState . appId , updates . searchKey ?? searchState . searchKey , updates . indexName ?? searchState . indexName ) ; searchDispatch ( updates ) ; } , [ ] ) ; useEffect ( ( ) => { setAlgoliaConfig ( { appId , searchKey , indexName } ) ; } , [ appId , searchKey , indexName ] ) ; return [ { ... searchState , request } , requestDispatch , getMore , setAlgoliaConfig , query , ] as [ typeof searchState & { request : typeof request ; } , typeof requestDispatch , typeof getMore , typeof setAlgoliaConfig , typeof query ] ; } export default useAlgolia ;