refactor(components): migrate to Ant Design Statistic.Timer and remove useRunOnce

- Deleted the custom `useRunOnce` hook as its functionality is no longer needed.
- Refactored `EventCard` to determine `type` (countup/countdown) dynamically based on the event state and current time.
- Updated `EventCard` to pass `type` and `format` separately to the `Statistic.Timer` component, removing the need for string formatting logic inside the component.
- Modified `ClubEventList` to add a 300ms delay on page initialization, ensuring the component is fully mounted before triggering the initial fetch logic.
This commit is contained in:
kyuuseiryuu 2026-03-15 08:42:53 +09:00
parent 0f9e80856b
commit 7103c5f7e3
3 changed files with 43 additions and 29 deletions

View File

@ -1,11 +1,10 @@
import { Button, Divider, Empty, Flex, Pagination, Skeleton, Space, Switch, Typography } from "antd"; import { Button, Divider, Empty, Flex, Pagination, Skeleton, Space, Switch, Typography } from "antd";
import type { IEventInfo } from "../types"; import type { IEventInfo } from "../types";
import { useCallback, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { EventCard } from "./EventCard"; import { EventCard } from "./EventCard";
import { useRequest } from "ahooks"; import { useRequest } from "ahooks";
import { CalendarOutlined } from "@ant-design/icons"; import { CalendarOutlined } from "@ant-design/icons";
import { STORE_PAGE_LIST_KEY } from "../utils/constants"; import { STORE_PAGE_LIST_KEY } from "../utils/constants";
import { useRunOnce } from "../hooks/useRunOnce";
interface Props { interface Props {
clubId: string; clubId: string;
@ -58,8 +57,13 @@ export const ClubEvenList = (props: Props) => {
setShowFinishedEvents(prePage !== 1 || isAllFinishedOrAllNotFinished); setShowFinishedEvents(prePage !== 1 || isAllFinishedOrAllNotFinished);
setPaginationControlVisible(prePage === 1 ? !isAllFinishedOrAllNotFinished : false); setPaginationControlVisible(prePage === 1 ? !isAllFinishedOrAllNotFinished : false);
} }
}, [props]); }, [props.clubId]);
useRunOnce(onPageInit); useEffect(() => {
const id = setTimeout(() => {
onPageInit();
}, 300);
return () => clearTimeout(id);
}, [onPageInit]);
const handleAddToCalendar = useCallback(() => { const handleAddToCalendar = useCallback(() => {
const url = `${window.location.origin}/api/club/${props.clubId}/calendar.ics`; const url = `${window.location.origin}/api/club/${props.clubId}/calendar.ics`;
const uri = url.replace(/^http(s)?/, 'webcal'); const uri = url.replace(/^http(s)?/, 'webcal');

View File

@ -6,7 +6,7 @@ import timezone from 'dayjs/plugin/timezone';
import { EyeOutlined } from "@ant-design/icons"; import { EyeOutlined } from "@ant-design/icons";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
import { useRunOnce } from "../hooks/useRunOnce"; import type { TimerType } from "antd/lib/statistic/Timer";
dayjs.extend(utc); dayjs.extend(utc);
dayjs.extend(timezone); dayjs.extend(timezone);
@ -23,24 +23,47 @@ export function EventCard(props: EventCardProps) {
navigate(`/event/${e.matchId}`); navigate(`/event/${e.matchId}`);
}, [e]); }, [e]);
const [messageFormat, setMessageFormat] = useState(''); const [messageFormat, setMessageFormat] = useState('');
const getMessageFormat = useCallback(() => { const getStatisticProps = useCallback((): {
format: string;
type: TimerType;
} => {
if (e.isFinished) { if (e.isFinished) {
if (dayjs().diff(day, 'days') < 1) return `已结束`; if (dayjs().diff(day, 'days') < 1) return {
return `已结束 DD 天`; type: 'countdown',
format: `已结束`,
};
return {
type: 'countup',
format: `已结束 DD 天`,
};
} }
if (e.isProcessing) { if (e.isProcessing) {
return '比赛进行中 HH:mm:ss'; return {
type: 'countup',
format: '比赛进行中 HH:mm:ss',
} }
return `距离比赛开始还有 DD 天 HH:mm:ss`; }
if (dayjs().diff(day, 'days') === 0) {
return {
type: 'countdown',
format: `距离比赛开始还有 HH:mm:ss`,
};
}
return {
type: 'countdown',
format: `距离比赛开始还有 DD 天 HH:mm:ss`,
};
}, [e, day]); }, [e, day]);
const [statisticType, setStatisticType] = useState<TimerType>('countdown');
const updateMessageFormat = useCallback(() => { const updateMessageFormat = useCallback(() => {
const format = getMessageFormat(); const statistic = getStatisticProps();
setMessageFormat(format); setMessageFormat(statistic.format);
console.debug('format: %s', format); setStatisticType(statistic.type);
}, [getMessageFormat]) console.debug('format: %s', day.format(statistic.format), statistic);
useRunOnce(updateMessageFormat); }, [getStatisticProps])
useEffect(() => { useEffect(() => {
const timeout = day.toDate().getTime() - Date.now(); const timeout = day.toDate().getTime() - Date.now();
updateMessageFormat();
const id = setTimeout(() => { const id = setTimeout(() => {
updateMessageFormat(); updateMessageFormat();
}, timeout); }, timeout);
@ -63,7 +86,7 @@ export function EventCard(props: EventCardProps) {
> >
<Typography.Text type={e.isFinished ? 'secondary' : 'success'}>{e.title}</Typography.Text> <Typography.Text type={e.isFinished ? 'secondary' : 'success'}>{e.title}</Typography.Text>
<Statistic.Timer <Statistic.Timer
type={e.isProcessing ? 'countup' : 'countdown'} type={statisticType}
value={day.toDate().getTime()} value={day.toDate().getTime()}
format={messageFormat} format={messageFormat}
styles={{ content: e.isFinished ? { color: 'gray' } : {} }} styles={{ content: e.isFinished ? { color: 'gray' } : {} }}

View File

@ -1,13 +0,0 @@
import { useEffect, useRef } from "react"
export const useRunOnce = (callback: () => void) => {
const done = useRef(false);
useEffect(() => {
const id = setTimeout(() => {
if (done.current) return;
done.current = true;
callback();
}, 0);
return () => clearTimeout(id);
}, [callback]);
}