diff --git a/src/CONST.ts b/src/CONST.ts index 7a8635895071..4d466dc3602b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1648,6 +1648,7 @@ const CONST = { LOCATION_UPDATE_INTERVAL: 5000, PLAY_SOUND_MESSAGE_DEBOUNCE_TIME: 500, SKELETON_ANIMATION_SPEED: 3, + SEARCH_OPTIONS_COMPARISON: 'search_options_comparison', }, PRIORITY_MODE: { GSD: 'gsd', diff --git a/src/hooks/useFastSearchFromOptions.ts b/src/hooks/useFastSearchFromOptions.ts index 41bb4f08965e..fad0e8873c86 100644 --- a/src/hooks/useFastSearchFromOptions.ts +++ b/src/hooks/useFastSearchFromOptions.ts @@ -1,10 +1,13 @@ import deburr from 'lodash/deburr'; -import {useCallback, useEffect, useState} from 'react'; +import {useCallback, useEffect, useRef, useState} from 'react'; +import Timing from '@libs/actions/Timing'; import FastSearch from '@libs/FastSearch'; import type {Options as OptionsListType, ReportAndPersonalDetailOptions} from '@libs/OptionsListUtils'; import {filterUserToInvite, isSearchStringMatch} from '@libs/OptionsListUtils'; +import Performance from '@libs/Performance'; import type {OptionData} from '@libs/ReportUtils'; import StringUtils from '@libs/StringUtils'; +import CONST from '@src/CONST'; type AllOrSelectiveOptions = ReportAndPersonalDetailOptions | OptionsListType; @@ -36,8 +39,17 @@ function useFastSearchFromOptions( {includeUserToInvite}: Options = {includeUserToInvite: false}, ): (searchInput: string) => AllOrSelectiveOptions { const [fastSearch, setFastSearch] = useState> | null>(null); + const prevOptionsRef = useRef(null); + const prevFastSearchRef = useRef> | null>(null); useEffect(() => { + const prevOptions = prevOptionsRef.current; + + if (prevOptions && shallowCompareOptions(prevOptions, options)) { + return; + } + prevOptionsRef.current = options; + prevFastSearchRef.current?.dispose(); const newFastSearch = FastSearch.createFastSearch([ { data: options.personalDetails, @@ -67,10 +79,11 @@ function useFastSearchFromOptions( }, ]); setFastSearch(newFastSearch); - - return () => newFastSearch.dispose(); + prevFastSearchRef.current = newFastSearch; }, [options]); + useEffect(() => () => prevFastSearchRef.current?.dispose(), []); + const findInSearchTree = useCallback( (searchInput: string): AllOrSelectiveOptions => { if (!fastSearch) { @@ -122,4 +135,40 @@ function useFastSearchFromOptions( return findInSearchTree; } +/** + * Compares two ReportAndPersonalDetailOptions objects shallowly. + * @returns true if the options are shallowly equal, false otherwise. + */ +function shallowCompareOptions(prev: ReportAndPersonalDetailOptions, next: ReportAndPersonalDetailOptions): boolean { + if (!prev || !next) { + return false; + } + + // Compare lengths first + if (prev.personalDetails.length !== next.personalDetails.length || prev.recentReports.length !== next.recentReports.length) { + return false; + } + Timing.start(CONST.TIMING.SEARCH_OPTIONS_COMPARISON); + Performance.markStart(CONST.TIMING.SEARCH_OPTIONS_COMPARISON); + + for (let i = 0; i < prev.personalDetails.length; i++) { + if (prev.personalDetails.at(i)?.keyForList !== next.personalDetails.at(i)?.keyForList) { + Timing.end(CONST.TIMING.SEARCH_OPTIONS_COMPARISON); + Performance.markEnd(CONST.TIMING.SEARCH_OPTIONS_COMPARISON); + return false; + } + } + + for (let i = 0; i < prev.recentReports.length; i++) { + if (prev.recentReports.at(i)?.keyForList !== next.recentReports.at(i)?.keyForList) { + Timing.end(CONST.TIMING.SEARCH_OPTIONS_COMPARISON); + Performance.markEnd(CONST.TIMING.SEARCH_OPTIONS_COMPARISON); + return false; + } + } + Timing.end(CONST.TIMING.SEARCH_OPTIONS_COMPARISON); + Performance.markEnd(CONST.TIMING.SEARCH_OPTIONS_COMPARISON); + return true; +} + export default useFastSearchFromOptions;