@gravityforms/components
Version:
UI components for use in Gravity Forms development. Both React and vanilla js flavors.
117 lines (106 loc) • 3.14 kB
JavaScript
import { React } from '@gravityforms/libraries';
import { debounce } from '@gravityforms/utils';
const { useEffect, useRef, useState } = React;
/**
* @module useAjaxSearch
* @description A hook to provide an interface for searching for dropdown list items using AJAX.
*
* @since 5.6.3
*
* @param {object} args The hook arguments.
* @param {Function} args.fetchListItems The async function to fetch and return the list items.
* @param {boolean} args.ignoreSameValue Whether to ignore the same search value for repeat requests.
* @param {Array} args.initialListItems The initial list items.
* @param {number} args.minChars The minimum number of characters before a search request is called.
* @param {number} args.searchDelay The delay in milliseconds before a search request is called.
*
* @return {object} The list items and search change function.
*/
const useAjaxSearch = ( {
fetchListItems = async () => {},
ignoreSameValue = true,
initialListItems = [],
minChars = 2,
searchDelay = 500,
} ) => {
const debounceRef = useRef( null );
const controllerRef = useRef( null );
const lastSearchValue = useRef( '' );
const [ isFetching, setIsFetching ] = useState( false );
const [ listItems, setListItems ] = useState( initialListItems );
/**
* @function fetchAndSetListItems
* @description Fetches and sets the list items.
*
* @since 5.6.3
*
* @async
*
* @param {string} value The search value.
* @param {object} signal The abort signal.
*
* @return {void}
*/
const fetchAndSetListItems = debounce( async ( value, signal ) => {
try {
setIsFetching( true );
const newListItems = await fetchListItems( value, { signal } );
setIsFetching( false );
setListItems( newListItems );
lastSearchValue.current = value;
} catch ( error ) {
if ( error.name !== 'AbortError' ) {
console.error( 'Error fetching list items for dropdown: ', error );
}
}
}, { wait: searchDelay } );
/**
* @function onSearch
* @description The search change function.
*
* @since 5.6.3
*
* @async
*
* @param {string} value The search value.
*
* @return {void}
*/
const onSearch = async ( value ) => {
// Clear the debounce timer.
if ( debounceRef.current ) {
clearTimeout( debounceRef.current );
}
// Abort the previous request.
if ( controllerRef.current ) {
controllerRef.current.abort();
}
const controller = new AbortController();
controllerRef.current = controller;
const signal = controller.signal;
// If the value is the same as the last search value, return.
if ( lastSearchValue.current === value && ignoreSameValue ) {
return;
}
// If the value is less than the minChars, return.
if ( value.length < minChars && value.length > 0 ) {
return;
}
fetchAndSetListItems( value, signal );
};
// Clean up controller when component unmounts.
useEffect( () => {
return () => {
if ( controllerRef.current ) {
controllerRef.current.abort();
}
};
}, [] );
return {
isFetching,
listItems,
onSearch,
setListItems,
};
};
export default useAjaxSearch;