React+TS+RTK Query中搜索排序工具函数未按预期工作排查
排序异常根因
- 核心逻辑漏洞:
String.indexOf()方法在字符串不包含目标子串时会返回-1,这个值比所有合法匹配位置(0、1、2……)数值更小。原排序规则是按匹配位置升序排列,导致所有不包含搜索词的卡牌反而被排在结果最前面,这就是前3条出现无关卡牌的直接原因。 - 排序比较器不符合规范:当两个卡牌名称字母序完全相等时,原代码直接返回
-1,不符合Array.sort()比较器的返回值要求(值相等时应返回0),会导致排序结果不稳定,出现预期外的乱序。 - 存在冗余计算:排序比较过程中重复对相同字符串执行
toLowerCase()和indexOf()计算,存在不必要的性能损耗。 - 边界场景缺失:未处理搜索词为空、接口返回数据结构异常的兜底逻辑,容易触发运行时报错。
修复实现
修复后的排序工具函数
import { CardData } from "../models/cardData.interface"; const SortNameBySearch = (search: string, cardData: CardData) => { // 异常场景兜底 if (!cardData?.data || !search.trim()) { return cardData?.data ?? []; } const keyword = search.toLowerCase(); const sortedData = [...cardData.data]; sortedData.sort((a, b) => { const nameA = a.name.toLowerCase(); const nameB = b.name.toLowerCase(); const posA = nameA.indexOf(keyword); const posB = nameB.indexOf(keyword); // 不匹配搜索词的项统一放到列表末尾 if (posA === -1 && posB !== -1) return 1; if (posB === -1 && posA !== -1) return -1; // 匹配项按搜索词出现位置升序排列 if (posA !== posB) return posA - posB; // 位置相同时按名称字母序排序,名称完全相等返回0保证排序稳定 return nameA.localeCompare(nameB); }); return sortedData; }; export default SortNameBySearch;
组件侧优化代码
import { FC, useEffect, useMemo, useRef, useState } from "react"; import { useGetCardsBySearchQuery } from "../../../../services/api"; import { SearchResultsProps } from "./SearchResults.types"; import SortNameBySearch from "../../../../utils/SortNamesBySearch"; import { Card } from "../../../../models/card.interface"; const SearchResults: FC<SearchResultsProps> = ({ search }) => { const [searched, setSearched] = useState(""); const searchRef = useRef<number | null>(null); useEffect(() => { if (searchRef.current) clearTimeout(searchRef.current); // 空搜索词清空已搜索值,不发起请求 if (!search?.trim()) { setSearched(""); return; } searchRef.current = window.setTimeout(() => { setSearched(search.trim()); }, 100); // 组件卸载时清理定时器,避免内存泄漏 return () => { if (searchRef.current) clearTimeout(searchRef.current); } }, [search]); const { data, isLoading, isError, isSuccess } = useGetCardsBySearchQuery( searched, { skip: !searched } ); // 缓存排序结果,仅当依赖项变化时重新计算,避免重复排序 const displayResults = useMemo(() => { if (!isSuccess || !data) return []; return SortNameBySearch(searched, data).slice(0, 3); }, [data, isSuccess, searched]); if (isError) { return <div>No card matching your query was found in the database.</div>; } if (isLoading || search !== searched) { return <>loading...</>; } return ( <div className="bg-black relative"> {displayResults.map((card: Card) => ( <p key={card.id} className="text-white"> {card.name} </p> ))} </div> ); }; export default SearchResults;
额外优化建议
- 可以扩展排序权重规则,比如和搜索词完全匹配的卡牌优先级最高,搜索词在名称开头的次之,再按出现位置靠后程度降权,搜索体验会更符合预期。
- 如果后续接口返回的卡牌量级变大,可以在排序前先过滤掉完全不包含搜索词的项,减少排序阶段的计算量。
- 当前使用的防抖逻辑和RTK Query的缓存机制没有冲突,不需要额外调整缓存配置。
内容的提问来源于stack exchange,提问作者Benji
相关产品推荐
相关产品推荐

