diff --git a/src/screens/SearchAll.tsx b/src/screens/SearchAll.tsx
index 3d062b4e..ebdcbed0 100644
--- a/src/screens/SearchAll.tsx
+++ b/src/screens/SearchAll.tsx
@@ -78,77 +78,104 @@ const compareSourcesBySearchResult = (
};
const TRIGGER_SEARCH_THRESHOLD = 1000; // ms
-const SourceSearchPreview = ({
- source,
- onSearchRequestFinished,
-}: {
- source: ISource;
- onSearchRequestFinished: (source: ISource, isLoading: boolean, hasResults: boolean, emptySearch: boolean) => void;
-}) => {
- const { t } = useTranslation();
- const [query] = useQueryParam('query', StringParam);
- const searchString = useDebounce(query, TRIGGER_SEARCH_THRESHOLD);
- const skipRequest = !searchString;
+const SourceSearchPreview = React.memo(
+ ({
+ source,
+ onSearchRequestFinished,
+ searchString,
+ emptyQuery,
+ }: {
+ source: ISource;
+ onSearchRequestFinished: (
+ source: ISource,
+ isLoading: boolean,
+ hasResults: boolean,
+ emptySearch: boolean,
+ ) => void;
+ searchString: string | null | undefined;
+ emptyQuery: boolean;
+ }) => {
+ const { t } = useTranslation();
+ const skipRequest = !searchString;
- const { id, displayName, lang } = source;
- const {
- data: searchResult,
- size,
- setSize,
- isLoading,
- error,
- } = requestManager.useSourceSearch(id, searchString ?? '', 1, { skipRequest });
- const mangas = !isLoading ? searchResult?.[0]?.mangaList ?? [] : [];
- const noMangasFound = !isLoading && !mangas.length;
+ const { id, displayName, lang } = source;
+ const {
+ data: searchResult,
+ size,
+ setSize,
+ isLoading,
+ error,
+ abortRequest,
+ } = requestManager.useSourceSearch(id, searchString ?? '', 1, { skipRequest });
+ const mangas = !isLoading ? searchResult?.[0]?.mangaList ?? [] : [];
+ const noMangasFound = !isLoading && !mangas.length;
- useEffect(() => {
- onSearchRequestFinished(source, isLoading, !noMangasFound, !searchString);
- }, [isLoading, noMangasFound, searchString]);
+ useEffect(() => {
+ onSearchRequestFinished(source, isLoading, !noMangasFound, !searchString);
+ }, [isLoading, noMangasFound, searchString]);
- if (!isLoading && !searchString) {
- return null;
- }
+ let errorMessage: string | undefined;
+ if (error) {
+ errorMessage = t('search.error.label.source_search_failed');
+ } else if (noMangasFound) {
+ errorMessage = t('manga.error.label.no_mangas_found');
+ }
- let errorMessage: string | undefined;
- if (error) {
- errorMessage = t('search.error.label.source_search_failed');
- } else if (noMangasFound) {
- errorMessage = t('manga.error.label.no_mangas_found');
- }
+ useEffect(
+ () => () => {
+ abortRequest(
+ new Error(`SourceSearchPreview(${source.id}, ${source.displayName}): search string changed`),
+ );
+ },
+ [searchString],
+ );
- return (
- <>
-
-
- {displayName}
- {translateExtensionLanguage(lang)}
-
-
-
- >
- );
-};
+ if ((!isLoading && !searchString) || emptyQuery) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+ {displayName}
+ {translateExtensionLanguage(lang)}
+
+
+
+ >
+ );
+ },
+);
const SearchAll: React.FC = () => {
const { t } = useTranslation();
const { setTitle, setAction } = useContext(NavbarContext);
+ const [query] = useQueryParam('query', StringParam);
+ const searchString = useDebounce(query, TRIGGER_SEARCH_THRESHOLD);
+
const [shownLangs, setShownLangs] = useLocalStorage('shownSourceLangs', sourceDefualtLangs());
const [showNsfw] = useLocalStorage('showNsfw', true);
const { data: sources = [] } = requestManager.useGetSourceList();
const [sourceToLoadingStateMap, setSourceToLoadingStateMap] = useState(new Map());
+ const debouncedSourceToLoadingStateMap = useDebounce(sourceToLoadingStateMap, 500);
const sourcesSortedByName = useMemo(() => [...sources].sort(compareSourceByName), [sources]);
const sourcesFilteredByLang = useMemo(
@@ -162,9 +189,9 @@ const SearchAll: React.FC = () => {
const sourcesSortedByResult = useMemo(
() =>
[...sourcesFilteredByNsfw].sort((sourceA, sourceB) =>
- compareSourcesBySearchResult(sourceA, sourceB, sourceToLoadingStateMap),
+ compareSourcesBySearchResult(sourceA, sourceB, debouncedSourceToLoadingStateMap),
),
- [sourcesFilteredByNsfw, sourceToLoadingStateMap],
+ [sourcesFilteredByNsfw, debouncedSourceToLoadingStateMap],
);
const updateSourceLoadingState = useCallback(
@@ -175,7 +202,7 @@ const SearchAll: React.FC = () => {
return mapCopy;
});
},
- [sourceToLoadingStateMap, setSourceToLoadingStateMap],
+ [setSourceToLoadingStateMap],
);
useEffect(() => {
@@ -208,6 +235,8 @@ const SearchAll: React.FC = () => {
key={source.id}
source={source}
onSearchRequestFinished={updateSourceLoadingState}
+ searchString={searchString}
+ emptyQuery={!query}
/>
))}
>