diff --git a/packages/hooks/src/useCountDown/demo/demo4.tsx b/packages/hooks/src/useCountDown/demo/demo4.tsx new file mode 100644 index 0000000000..c1717ff81e --- /dev/null +++ b/packages/hooks/src/useCountDown/demo/demo4.tsx @@ -0,0 +1,20 @@ +/** + * title: Configure the server time + * desc: Because users may change their local system time, the countdown can become inaccurate when using targetDate. + * + * title.zh-CN: 配置服务器时间 + * desc.zh-CN: 防止用户修改本地时间,导致配置 targetDate 时倒计时不准确,通过 currentServerTime 配置当前服务器时间 + */ + +import React, { useMemo } from "react"; +import { useCountDown } from "ahooks"; + +const App: React.FC = () => { + // should be get from server + const currentServerTime = useMemo(() => Date.now(), []); + + const [countdown] = useCountDown({ leftTime: 60 * 1000, currentServerTime }); + return
{countdown}
; +}; + +export default App; diff --git a/packages/hooks/src/useCountDown/index.en-US.md b/packages/hooks/src/useCountDown/index.en-US.md index 65f1bf8d32..dc18abca68 100644 --- a/packages/hooks/src/useCountDown/index.en-US.md +++ b/packages/hooks/src/useCountDown/index.en-US.md @@ -19,6 +19,10 @@ A hook for manage countdown.
+## Config currentServerTime
+
+
+
## API
```typescript
@@ -37,6 +41,7 @@ const [countdown, formattedRes] = useCountDown(
leftTime,
targetDate,
interval,
+ currentServerTime,
onEnd
}
);
@@ -53,6 +58,8 @@ If you only need to be accurate to the second, you can use it like this `Math.ro
If both `leftTime` and `targetDate` are passed, the `targetDate` is ignored, the `leftTime` is dominant.
+If 'currentServerTime' is not configured, the local time is used for the countdown.
+
### Params
| Property | Description | Type | Default |
@@ -60,6 +67,7 @@ If both `leftTime` and `targetDate` are passed, the `targetDate` is ignored, the
| leftTime | The rest of time, in milliseconds | `number` | - |
| targetDate | Target time | `TDate` | - |
| interval | Time interval between ticks, in milliseconds | `number` | `1000` |
+| currentServerTime | Current server time, in milliseconds | `number` | - |
| onEnd | Function to call when countdown completes | `() => void` | - |
### Return
@@ -71,4 +79,4 @@ If both `leftTime` and `targetDate` are passed, the `targetDate` is ignored, the
## Remark
-`leftTime`、`targetDate`、`interval`、`onEnd` support dynamic change.
+`leftTime`、`targetDate`、`interval`、`currentServerTime`、`onEnd` support dynamic change.
diff --git a/packages/hooks/src/useCountDown/index.ts b/packages/hooks/src/useCountDown/index.ts
index bcb8866b76..dfd77291c0 100644
--- a/packages/hooks/src/useCountDown/index.ts
+++ b/packages/hooks/src/useCountDown/index.ts
@@ -9,6 +9,7 @@ export interface Options {
leftTime?: number;
targetDate?: TDate;
interval?: number;
+ currentServerTime?: number;
onEnd?: () => void;
}
@@ -20,12 +21,23 @@ export interface FormattedRes {
milliseconds: number;
}
-const calcLeft = (target?: TDate) => {
+const calcLeft = (
+ target?: TDate,
+ momentTimeInfo?: {
+ serverTime: number;
+ localTime: number;
+ }
+) => {
if (!target) {
return 0;
}
// https://stackoverflow.com/questions/4310953/invalid-date-in-safari
- const left = dayjs(target).valueOf() - Date.now();
+ const left =
+ // 如果服务器时间存在,则使用服务器时间,并动态计算时间差值,否则使用本地时间
+ dayjs(target).valueOf() -
+ (momentTimeInfo?.serverTime
+ ? momentTimeInfo.serverTime + Date.now() - momentTimeInfo.localTime
+ : Date.now());
return left < 0 ? 0 : left;
};
@@ -40,15 +52,37 @@ const parseMs = (milliseconds: number): FormattedRes => {
};
const useCountdown = (options: Options = {}) => {
- const { leftTime, targetDate, interval = 1000, onEnd } = options || {};
+ const {
+ leftTime,
+ targetDate,
+ interval = 1000,
+ currentServerTime,
+ onEnd,
+ } = options || {};
+
+ /** 缓存此刻时间,保存当前服务器时间和本地时间,用于动态计算时间差,单位ms */
+ const momentTimeInfo = useMemo(
+ () => ({
+ serverTime: currentServerTime || 0,
+ localTime: currentServerTime ? Date.now() : 0,
+ }),
+ [currentServerTime]
+ );
const memoLeftTime = useMemo
+## 通过 currentServerTime 配置当前服务器时间
+
+
+
**说明**
useCountDown 的精度为毫秒,可能会造成以下几个问题
@@ -28,7 +32,9 @@ useCountDown 的精度为毫秒,可能会造成以下几个问题
如果你的精度只要到秒就好了,可以这样用 `Math.round(countdown / 1000)`。
-如果同时传了 `leftTime` 和 `targetDate`,则会忽略 `targetDate`,以 `leftTime` 为主
+如果同时传了 `leftTime` 和 `targetDate`,则会忽略 `targetDate`,以 `leftTime` 为主。
+
+如果没有配置 `currentServerTime` ,则会使用本地时间进行倒计时 。
## API
@@ -48,6 +54,7 @@ const [countdown, formattedRes] = useCountDown(
leftTime,
targetDate,
interval,
+ currentServerTime,
onEnd
}
);
@@ -60,6 +67,7 @@ const [countdown, formattedRes] = useCountDown(
| leftTime | 剩余时间(毫秒) | `number` | - |
| targetDate | 目标时间 | `TDate` | - |
| interval | 变化时间间隔(毫秒) | `number` | `1000` |
+| currentServerTime | 当前服务器时间(毫秒) | `number` | - |
| onEnd | 倒计时结束触发 | `() => void` | - |
### Result
@@ -71,4 +79,4 @@ const [countdown, formattedRes] = useCountDown(
## 备注
-`leftTime`、`targetDate`、`interval`、`onEnd` 支持动态变化
+`leftTime`、`targetDate`、`interval`、`currentServerTime`、`onEnd` 支持动态变化