From f49986ef8fbbb14aa71cbc5d9b27831de28535df Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Wed, 17 Dec 2025 15:34:36 +0800 Subject: [PATCH] =?UTF-8?q?refactor(Company):=20=E6=8F=90=E5=8F=96?= =?UTF-8?q?=E5=85=B1=E4=BA=AB=E7=9A=84=20useStockSearch=20Hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 useStockSearch.ts:统一股票模糊搜索逻辑 - 支持按代码或名称搜索 - 支持排除指定股票(用于对比场景) - 使用 useMemo 优化性能 - 重构 SearchBar.js:使用共享 Hook,减少 15 行代码 - 重构 CompareStockInput.tsx:使用共享 Hook,减少 20 行代码 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../components/CompanyHeader/SearchBar.js | 22 +++---- .../components/CompareStockInput.tsx | 34 ++++------- src/views/Company/hooks/useStockSearch.ts | 59 +++++++++++++++++++ 3 files changed, 76 insertions(+), 39 deletions(-) create mode 100644 src/views/Company/hooks/useStockSearch.ts diff --git a/src/views/Company/components/CompanyHeader/SearchBar.js b/src/views/Company/components/CompanyHeader/SearchBar.js index 677ebe00..f6989923 100644 --- a/src/views/Company/components/CompanyHeader/SearchBar.js +++ b/src/views/Company/components/CompanyHeader/SearchBar.js @@ -13,6 +13,7 @@ import { VStack, } from '@chakra-ui/react'; import { SearchIcon } from '@chakra-ui/icons'; +import { useStockSearch } from '../../hooks/useStockSearch'; /** * 股票搜索栏组件(带模糊搜索下拉) @@ -31,27 +32,18 @@ const SearchBar = ({ }) => { // 下拉状态 const [showDropdown, setShowDropdown] = useState(false); - const [filteredStocks, setFilteredStocks] = useState([]); const containerRef = useRef(null); // 从 Redux 获取全部股票列表 const allStocks = useSelector(state => state.stock.allStocks); - // 模糊搜索过滤 + // 使用共享的搜索 Hook + const filteredStocks = useStockSearch(allStocks, inputCode, { limit: 10 }); + + // 根据搜索结果更新下拉显示状态 useEffect(() => { - if (inputCode && inputCode.trim()) { - const searchTerm = inputCode.trim().toLowerCase(); - const filtered = allStocks.filter(stock => - stock.code.toLowerCase().includes(searchTerm) || - stock.name.includes(inputCode.trim()) - ).slice(0, 10); // 限制显示10条 - setFilteredStocks(filtered); - setShowDropdown(filtered.length > 0); - } else { - setFilteredStocks([]); - setShowDropdown(false); - } - }, [inputCode, allStocks]); + setShowDropdown(filteredStocks.length > 0 && !!inputCode?.trim()); + }, [filteredStocks, inputCode]); // 点击外部关闭下拉 useEffect(() => { diff --git a/src/views/Company/components/StockQuoteCard/components/CompareStockInput.tsx b/src/views/Company/components/StockQuoteCard/components/CompareStockInput.tsx index 94bf667e..92d9885e 100644 --- a/src/views/Company/components/StockQuoteCard/components/CompareStockInput.tsx +++ b/src/views/Company/components/StockQuoteCard/components/CompareStockInput.tsx @@ -18,6 +18,7 @@ import { } from '@chakra-ui/react'; import { SearchIcon } from '@chakra-ui/icons'; import { BarChart2 } from 'lucide-react'; +import { useStockSearch, type Stock } from '../../../hooks/useStockSearch'; interface CompareStockInputProps { onCompare: (stockCode: string) => void; @@ -25,11 +26,6 @@ interface CompareStockInputProps { currentStockCode?: string; } -interface Stock { - code: string; - name: string; -} - interface RootState { stock: { allStocks: Stock[]; @@ -43,7 +39,6 @@ const CompareStockInput: React.FC = ({ }) => { const [inputValue, setInputValue] = useState(''); const [showDropdown, setShowDropdown] = useState(false); - const [filteredStocks, setFilteredStocks] = useState([]); const [selectedStock, setSelectedStock] = useState(null); const containerRef = useRef(null); @@ -55,25 +50,16 @@ const CompareStockInput: React.FC = ({ const goldColor = '#F4D03F'; const bgColor = '#1A202C'; - // 模糊搜索过滤 + // 使用共享的搜索 Hook(排除当前股票) + const filteredStocks = useStockSearch(allStocks, inputValue, { + excludeCode: currentStockCode, + limit: 8, + }); + + // 根据搜索结果更新下拉显示状态 useEffect(() => { - if (inputValue && inputValue.trim()) { - const searchTerm = inputValue.trim().toLowerCase(); - const filtered = allStocks - .filter( - (stock) => - stock.code !== currentStockCode && // 排除当前股票 - (stock.code.toLowerCase().includes(searchTerm) || - stock.name.includes(inputValue.trim())) - ) - .slice(0, 8); // 限制显示8条 - setFilteredStocks(filtered); - setShowDropdown(filtered.length > 0); - } else { - setFilteredStocks([]); - setShowDropdown(false); - } - }, [inputValue, allStocks, currentStockCode]); + setShowDropdown(filteredStocks.length > 0 && !!inputValue?.trim()); + }, [filteredStocks, inputValue]); // 点击外部关闭下拉 useEffect(() => { diff --git a/src/views/Company/hooks/useStockSearch.ts b/src/views/Company/hooks/useStockSearch.ts new file mode 100644 index 00000000..cd7b7ff2 --- /dev/null +++ b/src/views/Company/hooks/useStockSearch.ts @@ -0,0 +1,59 @@ +/** + * useStockSearch - 股票模糊搜索 Hook + * + * 提取自 SearchBar.js 和 CompareStockInput.tsx 的共享搜索逻辑 + * 支持按代码或名称搜索,可选排除指定股票 + */ + +import { useMemo } from 'react'; + +export interface Stock { + code: string; + name: string; +} + +interface UseStockSearchOptions { + excludeCode?: string; + limit?: number; +} + +/** + * 股票模糊搜索 Hook + * + * @param allStocks - 全部股票列表 + * @param searchTerm - 搜索关键词 + * @param options - 可选配置 + * @param options.excludeCode - 排除的股票代码(用于对比场景) + * @param options.limit - 返回结果数量限制,默认 10 + * @returns 过滤后的股票列表 + */ +export const useStockSearch = ( + allStocks: Stock[], + searchTerm: string, + options: UseStockSearchOptions = {} +): Stock[] => { + const { excludeCode, limit = 10 } = options; + + return useMemo(() => { + const trimmed = searchTerm?.trim(); + if (!trimmed) return []; + + const term = trimmed.toLowerCase(); + + return allStocks + .filter((stock) => { + // 排除指定股票 + if (excludeCode && stock.code === excludeCode) { + return false; + } + // 按代码或名称匹配 + return ( + stock.code.toLowerCase().includes(term) || + stock.name.includes(trimmed) + ); + }) + .slice(0, limit); + }, [allStocks, searchTerm, excludeCode, limit]); +}; + +export default useStockSearch;