101 lines
2.7 KiB
JavaScript
101 lines
2.7 KiB
JavaScript
// src/hooks/useStockSearch.js
|
||
// 通用股票搜索 Hook - 支持代码、名称、拼音缩写搜索
|
||
|
||
import { useState, useCallback, useMemo, useEffect, useRef } from 'react';
|
||
import debounce from 'lodash/debounce';
|
||
import { getApiBase } from '@utils/apiConfig';
|
||
|
||
/**
|
||
* 股票搜索 Hook
|
||
* @param {Object} options 配置项
|
||
* @param {number} options.limit 返回结果数量限制,默认 10
|
||
* @param {number} options.debounceMs 防抖延迟,默认 300ms
|
||
* @param {Function} options.onSearch 搜索回调(用于追踪)
|
||
* @returns {Object} 搜索状态和方法
|
||
*/
|
||
export const useStockSearch = (options = {}) => {
|
||
const { limit = 10, debounceMs = 300, onSearch } = options;
|
||
|
||
// 使用 ref 存储 onSearch,避免因回调变化导致防抖函数重建
|
||
const onSearchRef = useRef(onSearch);
|
||
useEffect(() => {
|
||
onSearchRef.current = onSearch;
|
||
}, [onSearch]);
|
||
|
||
const [searchQuery, setSearchQuery] = useState('');
|
||
const [searchResults, setSearchResults] = useState([]);
|
||
const [isSearching, setIsSearching] = useState(false);
|
||
const [showResults, setShowResults] = useState(false);
|
||
|
||
// 调用 API 搜索
|
||
const searchStocks = useCallback(async (query) => {
|
||
if (!query || !query.trim()) {
|
||
setSearchResults([]);
|
||
setShowResults(false);
|
||
return;
|
||
}
|
||
|
||
setIsSearching(true);
|
||
try {
|
||
const response = await fetch(
|
||
`${getApiBase()}/api/stocks/search?q=${encodeURIComponent(query.trim())}&limit=${limit}`
|
||
);
|
||
const data = await response.json();
|
||
|
||
if (data.success && data.data) {
|
||
setSearchResults(data.data);
|
||
setShowResults(true);
|
||
// 触发搜索回调(用于追踪)
|
||
onSearchRef.current?.(query, data.data.length);
|
||
} else {
|
||
setSearchResults([]);
|
||
setShowResults(true);
|
||
onSearchRef.current?.(query, 0);
|
||
}
|
||
} catch (error) {
|
||
console.error('搜索股票失败:', error);
|
||
setSearchResults([]);
|
||
} finally {
|
||
setIsSearching(false);
|
||
}
|
||
}, [limit]);
|
||
|
||
// 防抖搜索
|
||
const debouncedSearch = useMemo(
|
||
() => debounce(searchStocks, debounceMs),
|
||
[searchStocks, debounceMs]
|
||
);
|
||
|
||
// 处理搜索输入
|
||
const handleSearch = useCallback((value) => {
|
||
setSearchQuery(value);
|
||
debouncedSearch(value);
|
||
}, [debouncedSearch]);
|
||
|
||
// 清空搜索
|
||
const clearSearch = useCallback(() => {
|
||
setSearchQuery('');
|
||
setSearchResults([]);
|
||
setShowResults(false);
|
||
}, []);
|
||
|
||
// 清理防抖
|
||
useEffect(() => {
|
||
return () => debouncedSearch.cancel?.();
|
||
}, [debouncedSearch]);
|
||
|
||
return {
|
||
// 状态
|
||
searchQuery,
|
||
searchResults,
|
||
isSearching,
|
||
showResults,
|
||
// 方法
|
||
handleSearch,
|
||
clearSearch,
|
||
setShowResults,
|
||
};
|
||
};
|
||
|
||
export default useStockSearch;
|