diff --git a/src/api/article.ts b/src/api/article.ts index 454d748..3815f6d 100755 --- a/src/api/article.ts +++ b/src/api/article.ts @@ -6,8 +6,8 @@ export const getArticleDataAPI = async (id: number, password?: string) => { return await Request
('GET', `/article${!password ? `/${id}` : `/${id}?password=${password}`}`); } -// 获取文章列表 -export const getArticlePagingAPI = async (params?: Page & { key?: string }) => { +// 获取文章列表(title 按标题模糊搜索) +export const getArticlePagingAPI = async (params?: Page & { title?: string }) => { return await Request>('GET', `/article`, { params: params ?? {} }); diff --git a/src/app/article/components/Comment/index.tsx b/src/app/article/components/Comment/index.tsx index 5d18d16..d40f6e5 100755 --- a/src/app/article/components/Comment/index.tsx +++ b/src/app/article/components/Comment/index.tsx @@ -242,7 +242,7 @@ const CommentForm = ({ articleId }: Props) => { ) : ( - )} diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index a587d15..efdea55 100755 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -5,7 +5,6 @@ import Link from 'next/link'; import { Modal, TextField, type DisclosureProps } from '@/ThriveUI'; import { getArticlePagingAPI } from '@/api/article'; import { Article } from '@/types/app/article'; -import useDebounce from '@/hooks/useDebounce'; import Empty from '../Empty'; interface Props { @@ -18,29 +17,6 @@ export default ({ disclosure }: Props) => { const [data, setData] = useState>(); const [searchKey, setSearchKey] = useState(''); - const getArticleList = async (key: string) => { - if (key.trim().length === 0) { - setData(undefined); - return; - } - - const { data } = await getArticlePagingAPI({ - key, - pageNum: 1, - pageSize: 10, - }); - - setData(data); - }; - - const debouncedFetchArticles = useDebounce(getArticleList, 300); - - const onSearchArticle = (e: React.ChangeEvent) => { - const key = e.target.value; - setSearchKey(key); - debouncedFetchArticles(key); - }; - useEffect(() => { if (!isOpen) { setData(undefined); @@ -48,14 +24,49 @@ export default ({ disclosure }: Props) => { } }, [isOpen]); + // 关键词变化时防抖请求;清理函数取消过期请求,避免清空后再搜被旧响应覆盖 + useEffect(() => { + if (!isOpen) return; + + const key = searchKey.trim(); + if (!key) { + setData(undefined); + return; + } + + let cancelled = false; + const timer = window.setTimeout(async () => { + try { + const { data: result } = await getArticlePagingAPI({ + title: key, + pageNum: 1, + pageSize: 10, + }); + if (!cancelled) setData(result); + } catch { + if (!cancelled) setData(undefined); + } + }, 300); + + return () => { + cancelled = true; + clearTimeout(timer); + }; + }, [searchKey, isOpen]); + return (
- + setSearchKey(e.target.value)} + />
- {data?.result - ? data?.result?.map((item) => ( + {data?.result?.length + ? data.result.map((item) => ( any>(func: T, wait: number) { - const timeoutRef = useRef(null); +/** 返回稳定的防抖函数;始终调用最新的 func,避免闭包过期 */ +export default function useDebounce unknown>(func: T, wait: number) { const funcRef = useRef(func); - funcRef.current = func; + const waitRef = useRef(wait); + const timeoutRef = useRef(undefined); - return (...args: Parameters) => { - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); - } - timeoutRef.current = window.setTimeout(() => { - funcRef.current(...args); - }, wait); - }; + useEffect(() => { + funcRef.current = func; + }, [func]); + + useEffect(() => { + waitRef.current = wait; + }, [wait]); + + useEffect(() => { + return () => { + if (timeoutRef.current !== undefined) clearTimeout(timeoutRef.current); + }; + }, []); + + const debouncedRef = useRef(null as unknown as T); + if (!debouncedRef.current) { + debouncedRef.current = ((...args: Parameters) => { + if (timeoutRef.current !== undefined) clearTimeout(timeoutRef.current); + timeoutRef.current = window.setTimeout(() => { + timeoutRef.current = undefined; + funcRef.current(...args); + }, waitRef.current); + }) as T; + } + + return debouncedRef.current; } diff --git a/src/lib/article.ts b/src/lib/article.ts index a3e7b0d..cf29f21 100644 --- a/src/lib/article.ts +++ b/src/lib/article.ts @@ -6,7 +6,7 @@ import { CACHE_TAGS } from '@/lib/cache-tags'; type ArticlePagingParams = { pageNum?: number; pageSize?: number; - key?: string; + title?: string; }; export async function getArticleCacheAPI(id: number) { @@ -25,19 +25,19 @@ export async function getArticlePagingCacheAPI(params: ArticlePagingParams = {}) 'use cache'; const pageNum = params.pageNum ?? 1; const pageSize = params.pageSize ?? 8; - const key = params.key ?? ''; + const title = params.title ?? ''; cacheLife('blog'); cacheTag( CACHE_TAGS.articles, `${CACHE_TAGS.articlesList}-${pageNum}-${pageSize}`, - ...(key ? [`${CACHE_TAGS.articlesList}-search-${key}`] : []), + ...(title ? [`${CACHE_TAGS.articlesList}-search-${title}`] : []), ); return getArticlePagingAPI({ pageNum, pageSize, - ...(key ? { key } : {}), + ...(title ? { title } : {}), }); }