Skip to content

Instantly share code, notes, and snippets.

@GriffinSauce
Last active November 28, 2020 18:25
Show Gist options
  • Select an option

  • Save GriffinSauce/0451d328511b4e82469f4e9f688e5118 to your computer and use it in GitHub Desktop.

Select an option

Save GriffinSauce/0451d328511b4e82469f4e9f688e5118 to your computer and use it in GitHub Desktop.

Revisions

  1. GriffinSauce revised this gist Feb 17, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion algolia-search-hook.js
    Original file line number Diff line number Diff line change
    @@ -16,7 +16,7 @@ const SHOW_LOADING_AFTER_MS = 300;
    export const useAlgoliaSearch = ({
    initialSearchState = {},
    initialResults = null, // For SSR
    onResults,
    onResults, // Eg. to track to analytics when results are shown
    }) => {
    if (!initialSearchState.index)
    throw new Error('initialSearchState with index is required');
  2. GriffinSauce revised this gist Feb 15, 2020. 1 changed file with 1 addition and 4 deletions.
    5 changes: 1 addition & 4 deletions algolia-search-hook.js
    Original file line number Diff line number Diff line change
    @@ -45,10 +45,7 @@ export const useAlgoliaSearch = ({
    // Get results
    try {
    const searchIndex = algoliaClient.initIndex(index);
    const res = await searchIndex.search({
    ...searchOptions,
    aroundPrecision: precisionFromRadius(searchOptions.aroundRadius), // Dynamically set geo buckets size that makes sense based on radius
    });
    const res = await searchIndex.search(searchOptions);
    setResults(res);
    clearTimeout(loadingTimeout);
    setLoading(false);
  3. GriffinSauce revised this gist Feb 15, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion algolia-search-hook.js
    Original file line number Diff line number Diff line change
    @@ -15,7 +15,7 @@ const SHOW_LOADING_AFTER_MS = 300;
    */
    export const useAlgoliaSearch = ({
    initialSearchState = {},
    initialResults = null,
    initialResults = null, // For SSR
    onResults,
    }) => {
    if (!initialSearchState.index)
  4. GriffinSauce created this gist Feb 15, 2020.
    113 changes: 113 additions & 0 deletions algolia-search-hook.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,113 @@
    import React, { useState, useEffect, useContext, useRef } from 'react';
    import algoliasearch from 'algoliasearch/lite';

    const algoliaClient = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_API_KEY);
    const SHOW_LOADING_AFTER_MS = 300;

    /**
    * Initialize a search
    *
    * @example:
    * const { searchState, setSearchState, results, isLoading } = useAlgoliaSearch();
    * const onSubmit = (query) => {
    * setSearchState(current => ({ ...current, query }));
    * }
    */
    export const useAlgoliaSearch = ({
    initialSearchState = {},
    initialResults = null,
    onResults,
    }) => {
    if (!initialSearchState.index)
    throw new Error('initialSearchState with index is required');
    const skip = useRef(!!initialResults);

    // The search query and options
    const [searchState, setSearchState] = useState({
    ...initialSearchState,
    context: initialSearchState.context || {}, // Put additional custom data in searchState.context - the search lib will keep it in state and won't touch it
    getRankingInfo: true,
    });
    const [results, setResults] = useState(initialResults);
    const [isLoading, setLoading] = useState(false);

    useEffect(
    () => {
    const { index, context, ...searchOptions } = searchState; // Algolia errors on unknown parameters so filter them out

    const getResults = async () => {
    // Show loading after a while to prevent jumpyness
    const loadingTimeout = setTimeout(
    () => setLoading(true),
    SHOW_LOADING_AFTER_MS,
    );

    // Get results
    try {
    const searchIndex = algoliaClient.initIndex(index);
    const res = await searchIndex.search({
    ...searchOptions,
    aroundPrecision: precisionFromRadius(searchOptions.aroundRadius), // Dynamically set geo buckets size that makes sense based on radius
    });
    setResults(res);
    clearTimeout(loadingTimeout);
    setLoading(false);
    if (onResults) onResults({ searchState, results: res });
    } catch (error) {
    if (error.message.toLowerCase().includes('unknown parameter')) {
    error.message = `${
    error.message
    } - if you want to add custom (non-Algolia) parameters, use searchState.context`;
    }
    console.error(`useAlgoliaSearch error: ${error.message}`);

    // Send error to sentry or whatever here

    clearTimeout(loadingTimeout);
    setLoading(false);
    }
    };

    // If initialResults is provided we can skip once
    if (skip.current) {
    skip.current = false;
    return;
    }
    getResults();
    },
    [searchState, onResults],
    );

    return {
    searchState,
    setSearchState,
    results,
    isLoading,
    };
    };

    /**
    * Context to access/mutate search down the component tree
    *
    * @example - Create the context:
    * const searchContext = useAlgoliaSearch();
    * retusn (
    * <SearchContext.Provider value={searchContext}>
    * <SearchBox />
    * </SearchContext.Provider>
    * );
    */
    export const SearchContext = React.createContext();

    /**
    * Hook to easily grab search props from context
    * Returns whatever is passed into SearchContext.Provider
    */
    export const useSearchContext = () => {
    const searchContext = useContext(SearchContext);
    if (!searchContext)
    throw new Error(
    'No search context found, make sure the component is wrapped with a <SearchContext.Provider>',
    );
    return searchContext;
    };