hooks#useDebounce TypeScript Examples
The following examples show how to use
hooks#useDebounce.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Input.tsx From frontend with GNU General Public License v3.0 | 5 votes |
Input = forwardRef<HTMLInputElement, IProps>(
(
{
type = 'text',
icon: Icon,
label,
placeholder,
onChange,
fullWidth,
error,
name,
debounce,
id,
...props
}: IProps,
ref
): JSX.Element => {
const debounceEffect = useDebounce();
const handleOnChange = (action: () => void) => {
if (!action) {
return;
}
debounce ? debounceEffect(action, debounce) : action();
};
const inputLabelId = label && id && `${id}-label`;
return (
<Container fullWidth={fullWidth} {...props}>
<InputWrapper fullWidth={fullWidth}>
{label && <Label htmlFor={inputLabelId}>{label}</Label>}
<StyledInput
id={inputLabelId}
isIcon={!!Icon}
type={type}
placeholder={placeholder}
onChange={() => handleOnChange(onChange)}
name={name}
ref={ref}
{...props}
/>
{Icon && (
<StyledIcon data-testid="icon">
<Icon />
</StyledIcon>
)}
</InputWrapper>
{error && console.log(error)}
</Container>
);
}
)
Example #2
Source File: ExploreApiSearch.tsx From substrate-api-explorer with Apache License 2.0 | 5 votes |
ExploreApiSearch = ({ focusOnMount, query, storybookDemo }: Props) => {
const historyHook = useHistory()
const history = storybookDemo ? {} : historyHook
const [searchQuery, setSearchQuery] = useState<string>(
query ? decodeURIComponent(query) : ''
)
const handleRedirectBack = () => {
if (storybookDemo) {
setSearchQuery('')
} else {
history.push({
pathname: '/explore-api',
state: { routeName: 'Explore API', fromSearch: true }
})
}
}
const handleSearch = () => {
if (!storybookDemo) {
if (decodeURIComponent(searchQuery).trim()) {
history.push({
pathname: `/search/${encodeURIComponent(searchQuery.trim())}`,
state: { routeName: 'Search' }
})
} else {
handleRedirectBack()
}
}
}
useDebounce(400, handleSearch, [searchQuery])
return (
<S.Wrapper
focusOnMount={focusOnMount}
placeholder="Search API..."
value={searchQuery}
onChange={e => setSearchQuery(e.target.value)}
onReset={handleRedirectBack}
/>
)
}
Example #3
Source File: APIListPage.tsx From one-platform with MIT License | 4 votes |
APIListPage = (): JSX.Element => {
const navigate = useNavigate();
// query param strings
const query = useQueryParams();
const mid = query.get('mid');
const defaultSearch = query.get('search');
// filters, search, sorting
const [isSortSelectOpen, setSortSelect] = useToggle();
const [sortOption, setSortOption] = useState(SortBy.RECENTLY_ADDED);
const [filters, setFilters] = useState<{ type: null | ApiCategory; search: string }>({
type: null,
search: defaultSearch || '',
});
const { pagination, onPerPageSelect, onSetPage, onResetPagination } = usePagination({
page: 1,
perPage: 20,
});
const debouncedSearch = useDebounce(filters.search);
// graphql query hooks
const { isLoading: isApiListLoading, data: namespaceList } = useGetNamespaceList({
limit: pagination.perPage,
offset: (pagination.page - 1) * pagination.perPage,
apiCategory: filters.type,
search: debouncedSearch,
sortBy: sortOption === SortBy.RECENTLY_ADDED ? 'CREATED_ON' : 'UPDATED_ON',
mid,
});
const { isLoading: isNamespaceStatLoading, data: namespaceStats } = useGetNamespaceStats({
search: debouncedSearch,
mid,
});
const handleApiOwnersRender = useCallback((owners: ApiOwnerType[]) => {
return owners.map((owner) =>
owner.group === ApiEmailGroup.USER ? owner.user.cn : owner.email
);
}, []);
const onStatCardClick = (cardType: 'total' | 'rest' | 'graphql') => {
onResetPagination();
if (cardType === 'total') {
setFilters((state) => ({ ...state, type: null }));
} else {
setFilters((state) => ({ ...state, type: cardType.toUpperCase() as ApiCategory }));
}
};
const onSearch = (search: string) => {
setFilters((state) => ({ ...state, search }));
};
const onSortSelect = (
event: React.MouseEvent | React.ChangeEvent,
value: string | SelectOptionObject,
isPlaceholder?: boolean
) => {
if (isPlaceholder) setSortOption(SortBy.RECENTLY_ADDED);
else setSortOption(value as SortBy);
setSortSelect.off(); // close the select
};
const onCardClick = (id: string) => {
navigate(id);
};
const namespaceCount = namespaceStats?.getApiCategoryCount;
const namespaces = namespaceList?.listNamespaces?.data;
const isNamespaceEmpty = !isApiListLoading && namespaces?.length === 0;
return (
<>
<Header />
<Divider />
<PageSection variant="light" isWidthLimited className="pf-m-align-center">
<Grid hasGutter>
<Grid hasGutter span={12}>
{stats.map(({ key, type, image }) => (
<GridItem
key={`api-select-${type}`}
span={4}
className={styles['api-list--stat-card']}
type={key}
>
<StatCard
value={namespaceCount?.[key]}
category={type}
isLoading={isNamespaceStatLoading}
onClick={callbackify(onStatCardClick, key)}
isSelected={filters.type ? filters.type.toLowerCase() === key : key === 'total'}
>
<img
src={`${config.baseURL}/images/${image}`}
alt={`api-select-${type}`}
style={{ height: '48px' }}
/>
</StatCard>
</GridItem>
))}
</Grid>
<GridItem className="pf-u-my-md">
<Split hasGutter className={styles['api-list--table-filter--container']}>
<SplitItem isFilled>
<Link to={ApiCatalogLinks.AddNewApiPage}>
<Button>Add API</Button>
</Link>
</SplitItem>
<SplitItem className="pf-u-w-33">
<Form>
<FormGroup fieldId="search">
<TextInput
aria-label="Search API"
placeholder="Search for APIs"
type="search"
iconVariant="search"
value={filters.search}
onChange={onSearch}
/>
</FormGroup>
</Form>
</SplitItem>
<SplitItem style={{ width: '180px' }}>
<Select
isOpen={isSortSelectOpen}
onToggle={setSortSelect.toggle}
selections={sortOption}
onSelect={onSortSelect}
>
{[
<SelectOption key="select-sort-placeholder" value="Sort by" isDisabled />,
<SelectOption
key={`select-sort:${SortBy.RECENTLY_ADDED}`}
value={SortBy.RECENTLY_ADDED}
/>,
<SelectOption
key={`select-sort:${SortBy.RECENTLY_MODIFIED}`}
value={SortBy.RECENTLY_MODIFIED}
/>,
]}
</Select>
</SplitItem>
</Split>
</GridItem>
{isApiListLoading ? (
<Bullseye className="pf-u-mt-lg">
<Spinner size="xl" />
</Bullseye>
) : (
namespaces?.map(({ id, name, updatedOn, owners, schemas, slug }) => (
<GridItem
span={12}
key={id}
className="catalog-nav-link"
onClick={callbackify(onCardClick, slug)}
>
<ApiDetailsCard
title={name}
owners={handleApiOwnersRender(owners)}
updatedAt={updatedOn}
schemas={schemas.map(({ name: schemaName, category }) => ({
name: schemaName,
type: category,
}))}
/>
</GridItem>
))
)}
{isNamespaceEmpty && (
<EmptyState>
<EmptyStateIcon icon={CubesIcon} />
<Title headingLevel="h4" size="lg">
No API found
</Title>
<EmptyStateBody>Add an API to fill this gap</EmptyStateBody>
</EmptyState>
)}
</Grid>
</PageSection>
<PageSection variant="light" isWidthLimited className="pf-m-align-center pf-u-pb-2xl">
<Pagination
itemCount={namespaceList?.listNamespaces?.count || 0}
widgetId="pagination-options-menu-bottom"
perPage={pagination.perPage}
page={pagination.page}
onSetPage={(_, page) => onSetPage(page)}
onPerPageSelect={(_, perPage) => onPerPageSelect(perPage)}
isCompact
/>
</PageSection>
</>
);
}
Example #4
Source File: HomePage.tsx From one-platform with MIT License | 4 votes |
HomePage = (): JSX.Element => {
const [search, setSearch] = useState('');
const navigate = useNavigate();
const debouncedSearch = useDebounce(search);
const { isLoading, data: namespace } = useGetNamespaceList({ search: debouncedSearch });
const onSearchChange = (value: string): void => {
setSearch(value);
};
const onSearch = () => {
navigate(`/apis?search=${search}`);
};
// memorized function to render the async search results
const searchResult = useMemo(() => {
const results = namespace?.listNamespaces?.data;
if (isLoading) {
return (
<MenuItem key="search-menu">
Searching <Spinner size="sm" />
</MenuItem>
);
}
if (!results?.length) {
return [
<MenuItem key="search-menu-no-result" isDisabled>
No results were found
</MenuItem>,
<MenuItem onClick={onSearch} itemId="loader" key="search-menu-view-more">
View more
</MenuItem>,
];
}
return results?.map(({ name, id, schemas, slug }, index) => [
<Link to={`/apis/${slug}`} className="catalog-nav-link" key={`${id}-${index + 1}`}>
<MenuItem
description={
<Split className="pf-u-mt-xs">
<SplitItem className="pf-u-mr-xs">
<Text component={TextVariants.small} className="pf-u-color-200">
Schema(s):
</Text>
</SplitItem>
{schemas.map(({ name: sName, id: sId, category }) => (
<SplitItem key={sId} className="pf-u-mr-xs">
<Label
isCompact
color="blue"
icon={
<img
src={category === ApiCategory.GRAPHQL ? GRAPHQL_LOGO : REST_LOGO}
width="12px"
alt="graphql rest"
className="pf-u-mt-xs"
/>
}
>
<Text component={TextVariants.small} className="pf-u-color-200">
{sName}
</Text>
</Label>
</SplitItem>
))}
</Split>
}
>
{name}
</MenuItem>
</Link>,
index !== (results?.length || 0) - 1 && <Divider key={`${id}-${index + 2}`} />,
index === (results?.length || 1) - 1 && (
<MenuItem onClick={onSearch} itemId="loader" key="search-menu-view-more">
View more
</MenuItem>
),
]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [namespace?.listNamespaces, isLoading]);
return (
<>
<PageSection className={css(styles.header, 'pf-m-align-center')} isWidthLimited>
<Stack className="pf-u-p-xl pf-u-pt-3xl" hasGutter>
<StackItem>
<Split>
<SplitItem isFilled>
<Stack style={{ maxWidth: '620px' }}>
<StackItem className="pf-u-mb-md">
<Title headingLevel="h1" size="4xl">
API Catalog
</Title>
</StackItem>
<StackItem>
<Text className="pf-u-font-size-xl">
A catalog of APIs to manage, promote and share APIs with developers and users.
</Text>
</StackItem>
<StackItem>
<Link to={ApiCatalogLinks.ListPage}>
<Button variant="primary" isBlock className={styles['header-explore-button']}>
<Split className="pf-u-align-items-center">
<SplitItem>Discover a wide list of APIs</SplitItem>
<SplitItem isFilled />
<SplitItem>Explore</SplitItem>
<SplitItem className="pf-u-ml-sm">
<ArrowRightIcon size="sm" />
</SplitItem>
</Split>
</Button>
</Link>
</StackItem>
</Stack>
</SplitItem>
<SplitItem>
<Bullseye>
<img src={OP_CONTAINER_LOGO} alt="api catalog" width="160cm" />
</Bullseye>
</SplitItem>
</Split>
</StackItem>
<StackItem className="pf-u-mt-2xl">
<Bullseye className={styles['catalog-search-container']}>
<div className="pf-u-w-75">
<SearchBar placeholder="Search for APIs" value={search} onChange={onSearchChange} />
</div>
{search && debouncedSearch && (
<Menu className={styles['catalog-search-menu']}>
<MenuList>{searchResult}</MenuList>
</Menu>
)}
</Bullseye>
</StackItem>
</Stack>
</PageSection>
<PageSection
isWidthLimited
className={css('pf-m-align-center', styles['add-api-section--container'])}
variant="dark"
>
<Bullseye className="pf-u-px-xl">
<Split className={css(styles['add-api-section'], 'pf-u-w-100 pf-u-px-4xl pf-u-py-xl')}>
<SplitItem isFilled>
<Text component={TextVariants.small}>ADD DATASOURCE</Text>
<Text className="pf-u-font-size-2xl">Want to add an API to the Catalog?</Text>
</SplitItem>
<SplitItem className="pf-l-flex pf-m-align-items-center">
<Link to={ApiCatalogLinks.AddNewApiPage}>
<Button variant="secondary">Add API</Button>
</Link>
</SplitItem>
</Split>
</Bullseye>
</PageSection>
</>
);
}
Example #5
Source File: HomePage.tsx From one-platform with MIT License | 4 votes |
HomePage = (): JSX.Element => {
const query = useQueryParams();
// modal state hooks
const { popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
{ name: 'detailView', isOpen: Boolean(query.get('id')) },
{ name: 'appList', isOpen: false },
]);
// hooks for api filtering, pagination
const [filters, setFilters] = useState<FeedbackFilters>({
selectedApps: null,
status: null,
category: null,
search: '',
});
const { pagination, onPerPageSelect, onSetPage } = usePagination();
const [isMyFeedback, setIsMyFeedback] = useToggle();
const [isExporting, setIsExporting] = useToggle();
const debouncedSearch = useDebounce(filters.search, 500);
// convert the filters to graphl variable format for api calls
const queryFilters = useMemo<GetFeedbackListQueryVariables>(() => {
const appIds = Object.keys(filters.selectedApps || {});
const userInfo = opcBase.auth?.getUserInfo();
return {
...filters,
search: debouncedSearch,
appId: appIds.length !== 0 ? appIds : null,
createdBy: isMyFeedback ? userInfo?.rhatUUID : null,
limit: pagination.perPage,
offset: (pagination.page - 1) * pagination.perPage,
};
}, [filters, debouncedSearch, isMyFeedback, pagination]);
// Get app list
const [{ data: appList, fetching: isAppListLoading }] = useQuery<GetAppsQuery>({
query: GetApps,
});
/**
* This query will be executed only when there is ?id=<feedback_id>
* To fetch a particular feedback
*/
const [{ data: feedbackById, fetching: isFeedbackByIdQueryLoading }] = useQuery<
GetFeedbackById,
{ id: string }
>({
query: GetFeedback,
variables: { id: query?.get('id') as string },
pause: !query?.get('id'),
});
// Get feedback list
const [{ data: fetchedFeedback, fetching: isFeedbackListLoading }] = useQuery<
GetFeedbackListQuery,
GetFeedbackListQueryVariables
>({
query: GetFeedbackList,
variables: queryFilters,
});
const feedbacks = fetchedFeedback?.listFeedbacks;
const selectedFeedback = (popUp?.detailView?.data as Feedback) || feedbackById?.getFeedbackById;
// To keep all selectedApps sorted to top
const selectedApps = useMemo(() => {
if (!appList?.apps) return [];
const apps = [...appList.apps];
return apps
.sort(({ id }) => (filters.selectedApps?.[id] ? -1 : 1)) // sort it with selected apps at top
.slice(0, Math.max(Object.keys(filters.selectedApps || {}).length, 5)); // either show 5 apps or all the selected ones
}, [appList?.apps, filters.selectedApps]);
/**
* To show the title of the ticket created
* Computes it based on JIRA, Github, Gitlab
*/
const formatePopupTitle = useCallback((type: string, url: string) => {
if (!type || !url) {
return '';
}
const splittedUrl = url.split('/');
const ticketName = splittedUrl[splittedUrl.length - 1];
if (type.toLowerCase() === 'jira') {
return ticketName;
}
return `${type} ${ticketName}`;
}, []);
const handleFeedbackFilterChange = useCallback(
<T extends unknown>(field: keyof FeedbackFilters, value: T) => {
setFilters((state) => ({ ...state, [field]: value }));
},
[]
);
const handleFeedbackFilterAppIdChange = useCallback(
(app: App) => {
const appsSelected = { ...filters.selectedApps };
if (appsSelected?.[app.id]) {
delete appsSelected[app.id];
} else {
appsSelected[app.id] = app;
}
setFilters((state) => ({ ...state, selectedApps: appsSelected }));
},
[filters.selectedApps]
);
const handleFeedbackFilterClear = useCallback((field: keyof FeedbackFilters) => {
setFilters((state) => ({ ...state, [field]: null }));
}, []);
const onExportToCSV = () => {
if (!feedbacks?.data) return;
setIsExporting.on();
/**
* Format the feedback list json response for the csv
* Set the headers and pick required fields only
*/
const formatedFeedbacks = feedbacks?.data.map((feedback: Record<string, unknown>) => {
const formatedFeedback: Record<string, unknown> = {};
EXPORT_FEEDBACK_CSV.forEach(({ title, field }) => {
const value = field.split('.').reduce((obj, i) => obj[i] as any, feedback);
formatedFeedback[title] = value;
});
return formatedFeedback;
});
jsonexport(formatedFeedbacks, (err, csv) => {
setIsExporting.off();
if (err) {
opcBase.toast.danger({ subject: 'Failed to export csv' });
} else {
// export to csv
let csvContent = 'data:text/csv;charset=utf-8,';
csvContent += csv;
const encodedCsv = encodeURI(csvContent);
const link = document.createElement('a');
link.setAttribute('href', encodedCsv);
link.setAttribute('download', 'Feedback.csv');
link.click();
opcBase.toast.success({
subject: 'Export sucessfully completed',
});
}
});
};
return (
<>
<PageSection isWidthLimited variant="light" className=" pf-m-align-center">
<Grid hasGutter style={{ '--pf-l-grid--m-gutter--GridGap': '2rem' } as CSSProperties}>
<GridItem span={3}>
<SearchInput
type="search"
id="search-feedback"
placeholder="Search via name"
value={filters.search || ''}
onChange={(value) => handleFeedbackFilterChange('search', value)}
/>
</GridItem>
<GridItem span={9}>
<Split>
<SplitItem isFilled>
<Button variant="primary" onClick={setIsMyFeedback.toggle}>
{`${isMyFeedback ? 'All' : 'My'} Feedback`}
</Button>
</SplitItem>
<SplitItem>
<Button
icon={<UploadIcon />}
variant="secondary"
onClick={onExportToCSV}
isLoading={isExporting}
isDisabled={!feedbacks?.count}
>
Export
</Button>
</SplitItem>
</Split>
</GridItem>
<GridItem span={3}>
<form>
<Stack hasGutter>
<StackItem>
<Stack hasGutter style={{ '--pf-global--gutter': '0.75rem' } as CSSProperties}>
<StackItem>
<FilterTitle
title="Applications"
onClear={() => handleFeedbackFilterClear('selectedApps')}
isClearable={Boolean(filters.selectedApps)}
/>
</StackItem>
{isAppListLoading ? (
<Bullseye>
<Spinner size="lg" label="Loading..." />
</Bullseye>
) : (
<>
{selectedApps.map((app) => (
<StackItem key={app.id}>
<Checkbox
id={app.id}
label={app.name}
className="capitalize"
isChecked={Boolean(filters.selectedApps?.[app.id])}
onChange={() => handleFeedbackFilterAppIdChange(app)}
/>
</StackItem>
))}
{(appList?.apps || [])?.length > 5 && (
<StackItem>
<Button
variant="link"
icon={<PlusIcon />}
onClick={() => handlePopUpOpen('appList')}
>
Expand to see more apps
</Button>
</StackItem>
)}
</>
)}
</Stack>
</StackItem>
<StackItem>
<Divider />
</StackItem>
<StackItem>
<Stack hasGutter style={{ '--pf-global--gutter': '0.75rem' } as CSSProperties}>
<StackItem>
<FilterTitle
title="Type"
onClear={() => handleFeedbackFilterClear('category')}
isClearable={Boolean(filters.category)}
/>
</StackItem>
<StackItem>
<Radio
id="feedback-type-1"
label="Bug"
name="type"
isChecked={filters?.category === FeedbackCategoryAPI.BUG}
onChange={() =>
handleFeedbackFilterChange('category', FeedbackCategoryAPI.BUG)
}
/>
</StackItem>
<StackItem>
<Radio
id="feedback-type-2"
label="Feedback"
name="type"
isChecked={filters?.category === FeedbackCategoryAPI.FEEDBACK}
onChange={() =>
handleFeedbackFilterChange('category', FeedbackCategoryAPI.FEEDBACK)
}
/>
</StackItem>
</Stack>
</StackItem>
<StackItem>
<Divider />
</StackItem>
<StackItem>
<Stack hasGutter style={{ '--pf-global--gutter': '0.75rem' } as CSSProperties}>
<StackItem>
<FilterTitle
title="Status"
onClear={() => handleFeedbackFilterClear('status')}
isClearable={Boolean(filters.status)}
/>
</StackItem>
<StackItem>
<Radio
id="feedback-status-1"
label="Open"
name="status"
isChecked={filters?.status === FeedbackStatusAPI.OPEN}
onChange={() =>
handleFeedbackFilterChange('status', FeedbackStatusAPI.OPEN)
}
/>
</StackItem>
<StackItem>
<Radio
id="feedback-status-2"
label="Closed"
name="status"
isChecked={filters?.status === FeedbackStatusAPI.CLOSED}
onChange={() =>
handleFeedbackFilterChange('status', FeedbackStatusAPI.CLOSED)
}
/>
</StackItem>
</Stack>
</StackItem>
<StackItem>
<Divider />
</StackItem>
</Stack>
</form>
</GridItem>
<GridItem span={9}>
<Stack hasGutter>
{isFeedbackListLoading || feedbacks?.count === 0 ? (
<EmptyState>
<EmptyStateIcon
variant={isFeedbackListLoading ? 'container' : 'icon'}
component={isFeedbackListLoading ? Spinner : undefined}
icon={CubesIcon}
/>
<Title size="lg" headingLevel="h4">
{isFeedbackListLoading ? 'Loading' : 'No feedback found!!'}
</Title>
</EmptyState>
) : (
feedbacks?.data?.map((feedback) => (
<StackItem key={feedback.id}>
<FeedbackCard
title={(feedback.createdBy as FeedbackUserProfileAPI)?.cn}
createdOn={feedback.createdOn}
description={feedback.summary}
experience={feedback.experience}
error={feedback.error}
module={feedback.module}
category={feedback.category}
state={feedback.state}
onClick={() => handlePopUpOpen('detailView', feedback)}
/>
</StackItem>
))
)}
<StackItem>
<Pagination
itemCount={feedbacks?.count}
isCompact
perPage={pagination.perPage}
page={pagination.page}
onSetPage={(_evt, newPage) => onSetPage(newPage)}
widgetId="feedback-pagination"
onPerPageSelect={(_evt, perPage) => onPerPageSelect(perPage)}
/>
</StackItem>
</Stack>
</GridItem>
</Grid>
</PageSection>
<Modal
variant={ModalVariant.small}
title={formatePopupTitle(selectedFeedback?.source, selectedFeedback?.ticketUrl)}
isOpen={popUp.detailView.isOpen}
onClose={() => handlePopUpClose('detailView')}
footer={
<Split hasGutter style={{ width: '100%' }}>
<SplitItem isFilled>
<a href={selectedFeedback?.ticketUrl} target="_blank" rel="noopener noreferrer">
<Button isSmall key="more" variant="danger">
See {selectedFeedback?.source} Issue
</Button>
</a>
</SplitItem>
<SplitItem>
<Button
key="close"
variant="tertiary"
isSmall
onClick={() => handlePopUpClose('detailView')}
>
Close
</Button>
</SplitItem>
</Split>
}
>
<FeedbackDetailCard feedback={selectedFeedback} isLoading={isFeedbackByIdQueryLoading} />
</Modal>
<Modal
variant={ModalVariant.medium}
title="All Applications"
isOpen={popUp.appList.isOpen}
onClose={() => handlePopUpClose('appList')}
>
<AppListCard
apps={appList?.apps}
filteredApps={filters.selectedApps}
onSubmit={(apps) => {
setFilters((state) => ({ ...state, selectedApps: apps }));
handlePopUpClose('appList');
}}
/>
</Modal>
</>
);
}