Skip to content

Commit

Permalink
UIF-562: add useDebouncedQuery hook to fix endless request for `Dyn…
Browse files Browse the repository at this point in the history
…amicSelection` component
  • Loading branch information
alisher-epam committed Nov 25, 2024
1 parent 30b44ad commit ebb87b3
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 41 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Add more reusable hooks and utilities. Refs UISACQCOMP-228.
* Move reusable version history components to the ACQ lib. Refs UISACQCOMP-230.
* Move reusable helper function to support version history functionality. Refs UISACQCOMP-232.
* Add `useDebouncedQuery` hook to fix endless request for `DynamicSelection` component. Refs UIF-562.

## [6.0.1](https://github.com/folio-org/stripes-acq-components/tree/v6.0.1) (2024-11-14)
[Full Changelog](https://github.com/folio-org/stripes-acq-components/compare/v6.0.0...v6.0.1)
Expand Down
63 changes: 22 additions & 41 deletions lib/DynamicSelection/DynamicSelection.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useCallback, useEffect, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { debounce } from 'lodash';
import PropTypes from 'prop-types';
import { useCallback } from 'react';
import { FormattedMessage } from 'react-intl';

import { Loading, Selection } from '@folio/stripes/components';
import { useOkapiKy } from '@folio/stripes/core';
import {
Loading,
Selection,
} from '@folio/stripes/components';

const LIST_ITEMS_LIMIT = 100;
const DEBOUNCE_DELAY = 500;
import { useDebouncedQuery } from '../hooks';

export const DynamicSelection = ({
api,
Expand All @@ -19,46 +19,27 @@ export const DynamicSelection = ({
value,
...rest
}) => {
const ky = useOkapiKy();
const [filterValue, setFilterValue] = useState('');
const [options, setOptions] = useState(initialOptions);
const [isLoading, setIsLoading] = useState();

const fetchData = useCallback(debounce(async (inputValue) => {
const searchParams = {
query: queryBuilder(inputValue),
limit: LIST_ITEMS_LIMIT,
};

try {
const res = await ky.get(api, { searchParams }).json();

setOptions(dataFormatter(res));
} catch {
setOptions([]);
}

setIsLoading(false);
}, DEBOUNCE_DELAY), []);

const onFilter = useCallback((inputValue) => {
setIsLoading(true);
setFilterValue(inputValue);
fetchData(inputValue);
const {
options = initialOptions,
isLoading,
inputValue,
setInputValue,
} = useDebouncedQuery({
api,
dataFormatter,
queryBuilder,
});

const onFilter = useCallback((filterValue) => {
setInputValue(filterValue);

return options;
}, [options, fetchData]);

useEffect(() => {
return () => {
fetchData.cancel();
};
}, []);
}, [options, setInputValue]);

return (
<Selection
dataOptions={options}
emptyMessage={!filterValue && <FormattedMessage id="stripes-acq-components.filter.dynamic.emptyMessage" />}
emptyMessage={!inputValue && <FormattedMessage id="stripes-acq-components.filter.dynamic.emptyMessage" />}
loading={isLoading}
loadingMessage={<Loading />}
name={name}
Expand Down
1 change: 1 addition & 0 deletions lib/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './useCampuses';
export * from './useCampusesQuery';
export * from './useCategories';
export * from './useContributorNameTypes';
export * from './useDebouncedQuery';
export * from './useDefaultReceivingSearchSettings';
export * from './useEventEmitter';
export * from './useExchangeRateValue';
Expand Down
1 change: 1 addition & 0 deletions lib/hooks/useDebouncedQuery/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useDebouncedQuery } from './useDebouncedQuery';
61 changes: 61 additions & 0 deletions lib/hooks/useDebouncedQuery/useDebouncedQuery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import debounce from 'lodash/debounce';
import {
useMemo,
useState,
} from 'react';
import { useQuery } from 'react-query';

import {
useNamespace,
useOkapiKy,
} from '@folio/stripes/core';

const LIST_ITEMS_LIMIT = 100;
const DEBOUNCE_DELAY = 500;

export const useDebouncedQuery = ({
api,
queryBuilder,
dataFormatter,
debounceDelay = DEBOUNCE_DELAY,
limit = LIST_ITEMS_LIMIT,
}) => {
const [inputValue, setInputValue] = useState('');
const [options, setOptions] = useState([]);
const ky = useOkapiKy();
const [namespace] = useNamespace({ key: 'locations' });

const debouncedSetInputValue = useMemo(() => {
return debounce((value) => setInputValue(value), debounceDelay);
}, [debounceDelay]);

const { isLoading } = useQuery({
queryKey: [namespace, inputValue],
queryFn: async ({ signal }) => {
if (!inputValue) return [];

const searchParams = {
query: queryBuilder(inputValue),
limit,
};

const res = await ky.get(api, { searchParams, signal }).json();

return dataFormatter(res);
},
enabled: Boolean(inputValue),
onSuccess: (data) => {
setOptions(data);
},
onError: () => {
setOptions([]);
},
});

return {
options,
isLoading,
inputValue,
setInputValue: debouncedSetInputValue,
};
};

0 comments on commit ebb87b3

Please sign in to comment.