- ErrorBoundary 组件:全局错误捕获与优雅降级 - 提取 5 个自定义 hooks:useCountUp, useDarkMode, useDebouncedValue, usePaginatedData, useApiRequest - 从 11 个 API 文件提取 PaginatedResponse 共享类型到 api/types.ts - 统一 API 错误处理(api/errors.ts) - client.ts 迁移到 axios adapter 模式(替代废弃的 CancelToken) - 添加 react-i18next 国际化基础设施 + zh-CN 语言包
37 lines
1.0 KiB
TypeScript
37 lines
1.0 KiB
TypeScript
import { useState, useCallback } from 'react';
|
|
import { message } from 'antd';
|
|
|
|
interface PaginatedState<T> {
|
|
data: T[];
|
|
total: number;
|
|
page: number;
|
|
loading: boolean;
|
|
}
|
|
|
|
export function usePaginatedData<T>(
|
|
fetchFn: (page: number, pageSize: number, search: string) => Promise<{ data: T[]; total: number }>,
|
|
pageSize = 20,
|
|
) {
|
|
const [state, setState] = useState<PaginatedState<T>>({
|
|
data: [],
|
|
total: 0,
|
|
page: 1,
|
|
loading: false,
|
|
});
|
|
const [searchText, setSearchText] = useState('');
|
|
|
|
const refresh = useCallback(async (p?: number) => {
|
|
const targetPage = p ?? state.page;
|
|
setState(s => ({ ...s, loading: true }));
|
|
try {
|
|
const result = await fetchFn(targetPage, pageSize, searchText);
|
|
setState({ data: result.data, total: result.total, page: targetPage, loading: false });
|
|
} catch {
|
|
message.error('加载数据失败');
|
|
setState(s => ({ ...s, loading: false }));
|
|
}
|
|
}, [fetchFn, pageSize, searchText, state.page]);
|
|
|
|
return { ...state, searchText, setSearchText, refresh };
|
|
}
|