- 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 语言包
25 lines
726 B
TypeScript
25 lines
726 B
TypeScript
import { useState, useEffect, useRef } from 'react';
|
|
|
|
export function useCountUp(end: number, duration = 800) {
|
|
const [count, setCount] = useState(0);
|
|
const prevEnd = useRef(end);
|
|
|
|
useEffect(() => {
|
|
if (end === prevEnd.current && count > 0) return;
|
|
prevEnd.current = end;
|
|
if (end === 0) { setCount(0); return; }
|
|
|
|
const startTime = performance.now();
|
|
function tick(now: number) {
|
|
const elapsed = now - startTime;
|
|
const progress = Math.min(elapsed / duration, 1);
|
|
const eased = 1 - Math.pow(1 - progress, 3);
|
|
setCount(Math.round(end * eased));
|
|
if (progress < 1) requestAnimationFrame(tick);
|
|
}
|
|
requestAnimationFrame(tick);
|
|
}, [end, duration]);
|
|
|
|
return count;
|
|
}
|