Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add recent serches and recent chats in Search router #49457

Merged
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0acddec
add SingleIconListItem
SzymczakJ Sep 17, 2024
682a8ec
add SearchRouterList
SzymczakJ Sep 17, 2024
386a8f7
Merge branch 'main' into @szymczak/serach-router-list
SzymczakJ Sep 18, 2024
5ed32cd
add find section to SearchRouterList
SzymczakJ Sep 18, 2024
0b3fe4b
add onSelectRow action
SzymczakJ Sep 19, 2024
3799068
clean up code
SzymczakJ Sep 19, 2024
2f3b0a5
add contextual search
SzymczakJ Sep 20, 2024
80370ca
fix big screen styling
SzymczakJ Sep 20, 2024
50d4e78
Merge branch 'main' into @szymczak/serach-router-list
SzymczakJ Sep 20, 2024
17cda40
fix pr comments
SzymczakJ Sep 24, 2024
7df4c32
Merge branch 'main' into @szymczak/serach-router-list
SzymczakJ Sep 24, 2024
48a4296
change contextual search logic
SzymczakJ Sep 25, 2024
96e309a
Merge branch 'main' into @szymczak/serach-router-list
SzymczakJ Sep 25, 2024
445a031
fix linter
SzymczakJ Sep 25, 2024
6e4d2b2
fix Enter shortcut logic
SzymczakJ Sep 26, 2024
7e509d5
fix SearchRouterList types
SzymczakJ Sep 26, 2024
4e439a2
fix SearchQueryListItem
SzymczakJ Sep 26, 2024
38ba637
fix typescript errors
SzymczakJ Sep 26, 2024
e9cba4e
fix styling
SzymczakJ Sep 26, 2024
cf57d6e
Merge branch 'main' into @szymczak/serach-router-list
SzymczakJ Sep 26, 2024
ab3109a
fix iOS
SzymczakJ Sep 27, 2024
48510b5
limit recentSearches amountto 5
SzymczakJ Sep 27, 2024
fb0fc7f
Merge branch 'main' into @szymczak/serach-router-list
SzymczakJ Sep 27, 2024
26e9026
fix PR comments
SzymczakJ Sep 27, 2024
e5ae868
add recent chat filtering logic
SzymczakJ Sep 30, 2024
b5f33da
fix PR coments
SzymczakJ Oct 1, 2024
7b111e8
Merge branch 'main' into @szymczak/serach-router-list
SzymczakJ Oct 1, 2024
66fa714
fix linter
SzymczakJ Oct 1, 2024
11525fa
fix PR comments
SzymczakJ Oct 1, 2024
994e17f
Merge branch 'main' into @szymczak/serach-router-list
SzymczakJ Oct 2, 2024
1b7f6ac
fix styles
SzymczakJ Oct 2, 2024
a986ab6
fix searchRouterLoading bug
SzymczakJ Oct 2, 2024
3b9e946
Merge branch 'main' into @szymczak/serach-router-list
SzymczakJ Oct 2, 2024
524eb8b
focus list item if it exists
SzymczakJ Oct 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5578,6 +5578,11 @@ const CONST = {
KEYWORD: 'keyword',
IN: 'in',
},
ROUTER_LIST_ITEM_TYPE: {
REPORT: 'report',
SEARCH: 'search',
CONTEXTUAL_SUGGESTION: 'contextualSuggestion',
},
},

REFERRER: {
Expand Down
4 changes: 4 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,9 @@ const ONYXKEYS = {
/** Stores the information about the saved searches */
SAVED_SEARCHES: 'nvp_savedSearches',

/** Stores the information about the recent searches */
RECENT_SEARCHES: 'nvp_recentSearches',

/** Stores recently used currencies */
RECENTLY_USED_CURRENCIES: 'nvp_recentlyUsedCurrencies',

Expand Down Expand Up @@ -850,6 +853,7 @@ type OnyxValuesMapping = {
// ONYXKEYS.NVP_TRYNEWDOT is HybridApp onboarding data
[ONYXKEYS.NVP_TRYNEWDOT]: OnyxTypes.TryNewDot;
[ONYXKEYS.SAVED_SEARCHES]: OnyxTypes.SaveSearch[];
[ONYXKEYS.RECENT_SEARCHES]: Record<string, OnyxTypes.RecentSearchItem>;
[ONYXKEYS.RECENTLY_USED_CURRENCIES]: string[];
[ONYXKEYS.ACTIVE_CLIENTS]: string[];
[ONYXKEYS.DEVICE_ID]: string;
Expand Down
123 changes: 91 additions & 32 deletions src/components/Search/SearchRouter/SearchRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,117 @@
import {useNavigationState} from '@react-navigation/native';
import debounce from 'lodash/debounce';
import React, {useCallback, useState} from 'react';
import React, {useCallback, useMemo, useState} from 'react';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal';
import Modal from '@components/Modal';
import {useOptionsList} from '@components/OptionListContextProvider';
import type {SearchQueryJSON} from '@components/Search/types';
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as SearchUtils from '@libs/SearchUtils';
import Navigation from '@navigation/Navigation';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import {useSearchRouterContext} from './SearchRouterContext';
import SearchRouterInput from './SearchRouterInput';
import SearchRouterList from './SearchRouterList';

const SEARCH_DEBOUNCE_DELAY = 150;

function SearchRouter() {
const styles = useThemeStyles();
const [betas] = useOnyx(`${ONYXKEYS.BETAS}`);
SzymczakJ marked this conversation as resolved.
Show resolved Hide resolved
const [recentSearches] = useOnyx(ONYXKEYS.RECENT_SEARCHES);

const {isSmallScreenWidth} = useResponsiveLayout();
const {isSearchRouterDisplayed, closeSearchRouter} = useSearchRouterContext();

const [textInputValue, setTextInputValue] = useState('');
const [userSearchQuery, setUserSearchQuery] = useState<SearchQueryJSON | undefined>(undefined);
const contextualReportID = useNavigationState<Record<string, {reportID: string}>, string | undefined>((state) => {
return state.routes.at(-1)?.params?.reportID;
});
const sortedRecentSearches = useMemo(() => {
return Object.values(recentSearches ?? {}).sort((a, b) => {
const dateA = new Date(a.timestamp);
const dateB = new Date(b.timestamp);
return dateB.getTime() - dateA.getTime();
SzymczakJ marked this conversation as resolved.
Show resolved Hide resolved
});
}, [recentSearches]);

const {options, areOptionsInitialized} = useOptionsList({
shouldInitialize: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
shouldInitialize: true,
shouldInitialize: didScreenTransitionEnd,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering, if I should add it but in the end I don't know why do we even need to pass shouldInitialize. I don't understand why do we need to wait for transitionEnd to initialize some data in context. I also didn't notice any bugs when having this set to true. Maybe you have some more context @rayane-djouah

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Modal is also outside any ScreenWrapperContext so it does not have didScreenTransitionEnd

});
const searchOptions = useMemo(() => {
if (!areOptionsInitialized) {
return {recentReports: [], personalDetails: [], userToInvite: null, currentUserOption: null, categoryOptions: [], tagOptions: [], taxRatesOptions: []};
}
return OptionsListUtils.getSearchOptions(options, '', betas ?? []);
SzymczakJ marked this conversation as resolved.
Show resolved Hide resolved
}, [areOptionsInitialized, betas, options]);

const contextualReportData = contextualReportID ? searchOptions.recentReports?.find((option) => option.reportID === contextualReportID) : undefined;

const clearUserQuery = () => {
setTextInputValue('');
setUserSearchQuery(undefined);
};

const onSearchChange = debounce((userQuery: string) => {
if (!userQuery) {
clearUserQuery();
return;
}

const queryJSON = SearchUtils.buildSearchQueryJSON(userQuery);

if (queryJSON) {
// eslint-disable-next-line
console.log('parsedQuery', queryJSON);

setUserSearchQuery(queryJSON);
} else {
// Handle query parsing error
}
}, SEARCH_DEBOUNCE_DELAY);
const onSearchChange = useCallback(

Check warning on line 64 in src/components/Search/SearchRouter/SearchRouter.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead

Check warning on line 64 in src/components/Search/SearchRouter/SearchRouter.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead
debounce((userQuery: string) => {

Check failure on line 65 in src/components/Search/SearchRouter/SearchRouter.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Expected the first argument to be an inline function expression

Check failure on line 65 in src/components/Search/SearchRouter/SearchRouter.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Expected the first argument to be an inline function expression
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SzymczakJ
this construct useCallback(debounce(... will not pass our linting. I know because I faced this problem in the past.
Feel free to read more about this lint rule.

The solution will be either to drop useCallback for now, and make sure the function inside is not too computation-heavy, or eslint-ignore this line.
Alternatively check this article: https://kyleshevlin.com/debounce-and-throttle-callbacks-with-react-hooks/

For the record I think this warning is more like:

eslint cannot figure out what this function argument are, so it shouts a warning

than

this is bad react code

if (!userQuery) {
clearUserQuery();
return;
}

const queryJSON = SearchUtils.buildSearchQueryJSON(userQuery);

if (queryJSON) {
// eslint-disable-next-line
console.log('parsedQuery', queryJSON);

setUserSearchQuery(queryJSON);
} else {
// Handle query parsing error
}
}, SEARCH_DEBOUNCE_DELAY),
[],
);

const onSearchSubmit = useCallback(() => {
if (!userSearchQuery) {
return;
}
const updateUserSearchQuery = (newSearchQuery: string) => {
setTextInputValue(newSearchQuery);
onSearchChange(newSearchQuery);
};

const closeAndClearRouter = useCallback(() => {
closeSearchRouter();

const query = SearchUtils.buildSearchQueryString(userSearchQuery);
Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query}));

clearUserQuery();
}, [closeSearchRouter, userSearchQuery]);
}, [closeSearchRouter]);

const onSearchSubmit = useCallback(
(query: SearchQueryJSON | undefined) => {
if (!query) {
return;
}
closeSearchRouter();
const queryString = SearchUtils.buildSearchQueryString(query);
Navigation.navigate(ROUTES.SEARCH_CENTRAL_PANE.getRoute({query: queryString}));
clearUserQuery();
},
[closeSearchRouter],
);

useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ESCAPE, () => {
closeSearchRouter();
clearUserQuery();
});

const modalType = isSmallScreenWidth ? CONST.MODAL.MODAL_TYPE.CENTERED : CONST.MODAL.MODAL_TYPE.POPOVER;
const isFullWidth = isSmallScreenWidth;
const modalWidth = isSmallScreenWidth ? styles.w100 : {width: variables.popoverWidth};

return (
<Modal
Expand All @@ -76,11 +122,24 @@
onClose={closeSearchRouter}
>
<FocusTrapForModal active={isSearchRouterDisplayed}>
<View style={[styles.flex1, styles.p3]}>
<View style={[styles.flex1, styles.p3, modalWidth, styles.mh100, !isSmallScreenWidth && styles.mh85vh]}>
<SearchRouterInput
isFullWidth={isFullWidth}
onChange={onSearchChange}
onSubmit={onSearchSubmit}
text={textInputValue}
setText={setTextInputValue}
updateSearch={onSearchChange}
onSubmit={() => {
onSearchSubmit(userSearchQuery);
}}
/>

<SearchRouterList
SzymczakJ marked this conversation as resolved.
Show resolved Hide resolved
currentQuery={userSearchQuery}
reportForContextualSearch={contextualReportData}
recentSearches={sortedRecentSearches}
recentReports={searchOptions?.recentReports?.slice(0, 5)}
SzymczakJ marked this conversation as resolved.
Show resolved Hide resolved
onSearchSubmit={onSearchSubmit}
updateUserSearchQuery={updateUserSearchQuery}
closeAndClearRouter={closeAndClearRouter}
/>
</View>
</FocusTrapForModal>
Expand Down
20 changes: 8 additions & 12 deletions src/components/Search/SearchRouter/SearchRouterInput.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
import React, {useState} from 'react';

Check failure on line 1 in src/components/Search/SearchRouter/SearchRouterInput.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'useState' is defined but never used

Check failure on line 1 in src/components/Search/SearchRouter/SearchRouterInput.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'useState' is defined but never used
import BaseTextInput from '@components/TextInput/BaseTextInput';
import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import CONST from '@src/CONST';

type SearchRouterInputProps = {
isFullWidth: boolean;
onChange: (searchTerm: string) => void;
text: string;
setText: (searchTerm: string) => void;
updateSearch: (searchTerm: string) => void;
onSubmit: () => void;
};

function SearchRouterInput({isFullWidth, onChange, onSubmit}: SearchRouterInputProps) {
function SearchRouterInput({text, setText, updateSearch, onSubmit}: SearchRouterInputProps) {
const styles = useThemeStyles();

const [value, setValue] = useState('');

const onChangeText = (text: string) => {

Check failure on line 16 in src/components/Search/SearchRouter/SearchRouterInput.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'text' is already declared in the upper scope on line 13 column 29

Check failure on line 16 in src/components/Search/SearchRouter/SearchRouterInput.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'text' is already declared in the upper scope on line 13 column 29
setValue(text);
onChange(text);
setText(text);
updateSearch(text);
};

const modalWidth = isFullWidth ? styles.w100 : {width: variables.popoverWidth};

return (
<BaseTextInput
value={value}
value={text}
SzymczakJ marked this conversation as resolved.
Show resolved Hide resolved
onChangeText={onChangeText}
onSubmitEditing={onSubmit}
autoFocus
textInputContainerStyles={[{borderBottomWidth: 0}, modalWidth]}
textInputContainerStyles={[{borderBottomWidth: 0}, styles.w100]}
inputStyle={[styles.searchInputStyle, styles.searchRouterInputStyle, styles.ph2]}
role={CONST.ROLE.PRESENTATION}
autoCapitalize="none"
Expand Down
141 changes: 141 additions & 0 deletions src/components/Search/SearchRouter/SearchRouterList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import React, {useCallback} from 'react';
import * as Expensicons from '@components/Icon/Expensicons';
import type {SearchQueryJSON, SearchRouterListItem} from '@components/Search/types';
import SelectionList from '@components/SelectionList';
import SingleIconListItem from '@components/SelectionList/Search/SingleIconListItem';
import type {ListItemWithSingleIcon, SingleIconListItemProps} from '@components/SelectionList/Search/SingleIconListItem';
import type {SectionListDataType, UserListItemProps} from '@components/SelectionList/types';
import UserListItem from '@components/SelectionList/UserListItem';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import type {OptionData} from '@libs/ReportUtils';
import * as SearchUtils from '@libs/SearchUtils';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';

type ItemWithQuery = {
query: string;
};

type SearchRouterListProps = {
currentQuery: SearchQueryJSON | undefined;
reportForContextualSearch?: OptionData;
recentSearches: ItemWithQuery[] | undefined;
recentReports: OptionData[];
onSearchSubmit: (query: SearchQueryJSON | undefined) => void;
updateUserSearchQuery: (newSearchQuery: string) => void;
closeAndClearRouter: () => void;
};

function SearchRouterItem(props: UserListItemProps<SearchRouterListItem> | SingleIconListItemProps<SearchRouterListItem>) {
const styles = useThemeStyles();
if (props.item.itemType === CONST.SEARCH.ROUTER_LIST_ITEM_TYPE.REPORT) {
return (
<UserListItem
pressableStyle={styles.br2}
// eslint-disable-next-line react/jsx-props-no-spreading
{...(props as UserListItemProps<OptionData>)}
/>
);
}
// eslint-disable-next-line react/jsx-props-no-spreading
return <SingleIconListItem {...(props as SingleIconListItemProps<ListItemWithSingleIcon & ItemWithQuery>)} />;
SzymczakJ marked this conversation as resolved.
Show resolved Hide resolved
}

function SearchRouterList({currentQuery, reportForContextualSearch, recentSearches, recentReports, onSearchSubmit, updateUserSearchQuery, closeAndClearRouter}: SearchRouterListProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const sections: Array<SectionListDataType<SearchRouterListItem>> = [];

if (currentQuery?.inputQuery) {
sections.push({
data: [
{
text: currentQuery?.inputQuery,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here for the input query:

Suggested change
text: currentQuery?.inputQuery,
text: SearchUtils.getSearchHeaderTitle(currentQuery?.inputQuery, personalDetails, cardList, reports, taxRates);,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to do that in this case? If we change it that way the input will be different than a preselected option which may confuse the user.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should keep the input values as is (without changing them). This will be solved by autocomplete in Search v2.5

singleIcon: Expensicons.MagnifyingGlass,
query: currentQuery?.inputQuery,
itemStyle: styles.activeComponentBG,
keyForList: 'findItem',
itemType: CONST.SEARCH.ROUTER_LIST_ITEM_TYPE.SEARCH,
},
],
});
}

if (reportForContextualSearch) {
sections.push({
data: [
{
text: `${translate('search.searchIn')}${reportForContextualSearch.text ?? reportForContextualSearch.alternateText}`,
SzymczakJ marked this conversation as resolved.
Show resolved Hide resolved
singleIcon: Expensicons.MagnifyingGlass,
query: `in:${reportForContextualSearch.reportID}`,
SzymczakJ marked this conversation as resolved.
Show resolved Hide resolved
itemStyle: styles.activeComponentBG,
keyForList: 'contextualSearch',
itemType: CONST.SEARCH.ROUTER_LIST_ITEM_TYPE.CONTEXTUAL_SUGGESTION,
},
],
});
}

const recentSearchesData = recentSearches?.map(({query}) => ({
text: query,
SzymczakJ marked this conversation as resolved.
Show resolved Hide resolved
singleIcon: Expensicons.History,
query,
keyForList: query,
itemType: CONST.SEARCH.ROUTER_LIST_ITEM_TYPE.SEARCH,
}));

if (recentSearchesData && recentSearchesData.length > 0) {
sections.push({title: translate('search.recentSearches'), data: recentSearchesData});
}

const recentReportsData = recentReports.map((item) => ({...item, pressableStyle: styles.br2, itemType: CONST.SEARCH.ROUTER_LIST_ITEM_TYPE.REPORT}));
sections.push({title: translate('search.recentChats'), data: recentReportsData});

const onSelectRow = useCallback(
(item: SearchRouterListItem) => {
switch (item.itemType) {

Check failure on line 99 in src/components/Search/SearchRouter/SearchRouterList.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Expected a default case

Check failure on line 99 in src/components/Search/SearchRouter/SearchRouterList.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Switch is not exhaustive. Cases not matched: undefined

Check failure on line 99 in src/components/Search/SearchRouter/SearchRouterList.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Expected a default case

Check failure on line 99 in src/components/Search/SearchRouter/SearchRouterList.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Switch is not exhaustive. Cases not matched: undefined
case CONST.SEARCH.ROUTER_LIST_ITEM_TYPE.SEARCH:
// Handle selection of "Recent search"
if (!('query' in item) || !item?.query) {
return;
}
const queryJSON = SearchUtils.buildSearchQueryJSON(item?.query);

Check failure on line 105 in src/components/Search/SearchRouter/SearchRouterList.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Unexpected lexical declaration in case block

Check failure on line 105 in src/components/Search/SearchRouter/SearchRouterList.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Unexpected lexical declaration in case block
onSearchSubmit(queryJSON);
return;
case CONST.SEARCH.ROUTER_LIST_ITEM_TYPE.CONTEXTUAL_SUGGESTION:
// Handle selection of "Contextual search suggestion"
if (!('query' in item) || !item?.query || currentQuery?.inputQuery.includes(item?.query)) {
return;
}
updateUserSearchQuery(`${item?.query} ${currentQuery?.inputQuery ?? ''}`);
return;
case CONST.SEARCH.ROUTER_LIST_ITEM_TYPE.REPORT:
// Handle selection of "Recent chat"
closeAndClearRouter();
if ('reportID' in item && item?.reportID) {
Navigation.closeAndNavigate(ROUTES.REPORT_WITH_ID.getRoute(item?.reportID));
} else if ('login' in item) {
Report.navigateToAndOpenReport(item?.login ? [item.login] : []);
}
return;

Check failure on line 123 in src/components/Search/SearchRouter/SearchRouterList.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Unnecessary return statement

Check failure on line 123 in src/components/Search/SearchRouter/SearchRouterList.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Unnecessary return statement
}
},
[closeAndClearRouter, onSearchSubmit, currentQuery],

Check warning on line 126 in src/components/Search/SearchRouter/SearchRouterList.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

React Hook useCallback has a missing dependency: 'updateUserSearchQuery'. Either include it or remove the dependency array. If 'updateUserSearchQuery' changes too often, find the parent component that defines it and wrap that definition in useCallback

Check warning on line 126 in src/components/Search/SearchRouter/SearchRouterList.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

React Hook useCallback has a missing dependency: 'updateUserSearchQuery'. Either include it or remove the dependency array. If 'updateUserSearchQuery' changes too often, find the parent component that defines it and wrap that definition in useCallback
);

return (
<SelectionList<SearchRouterListItem>
sections={sections}
onSelectRow={onSelectRow}
ListItem={SearchRouterItem}
containerStyle={styles.mh100}
/>
);
}

export default SearchRouterList;
export {SearchRouterItem};
export type {ItemWithQuery};
Loading
Loading