Skip to content

Commit 8103b9f

Browse files
committed
fix hover pause timer
1 parent ce28148 commit 8103b9f

2 files changed

Lines changed: 33 additions & 10 deletions

File tree

src/hooks/useNoticeTimer.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,44 @@ export default function useNoticeTimer(
1818
const onEventUpdate = useEvent(onUpdate);
1919

2020
const [walking, setWalking] = React.useState(durationMs > 0);
21-
const startTimestampRef = React.useRef<number | null>(null);
2221
const passTimeRef = React.useRef(0);
22+
const lastRafTimeRef = React.useRef<number | null>(null);
2323

2424
function syncPassTime() {
2525
const now = Date.now();
26-
const passedTime = now - (startTimestampRef.current || now);
27-
startTimestampRef.current = now;
28-
passTimeRef.current += passedTime;
26+
const lastRafTime = lastRafTimeRef.current;
27+
28+
if (lastRafTime !== null) {
29+
passTimeRef.current += now - lastRafTime;
30+
}
31+
32+
lastRafTimeRef.current = now;
2933
}
3034

3135
const onPause = React.useCallback(() => {
3236
syncPassTime();
37+
if (durationMs > 0) {
38+
onEventUpdate(Math.min(passTimeRef.current / durationMs, 1));
39+
}
40+
lastRafTimeRef.current = null;
3341
setWalking(false);
34-
}, []);
42+
// `onEventUpdate` is a `useEvent` callback and always reads the latest value.
43+
// eslint-disable-next-line react-hooks/exhaustive-deps
44+
}, [durationMs]);
3545

3646
const onResume = React.useCallback(() => {
3747
if (durationMs > 0) {
3848
setWalking(true);
3949
} else {
4050
onEventUpdate(0);
4151
}
42-
}, [durationMs, onEventUpdate]);
52+
// `onEventUpdate` is a `useEvent` callback and always reads the latest value.
53+
// eslint-disable-next-line react-hooks/exhaustive-deps
54+
}, [durationMs]);
4355

4456
React.useEffect(() => {
4557
if (durationMs <= 0) {
46-
startTimestampRef.current = null;
58+
lastRafTimeRef.current = null;
4759
onEventUpdate(0);
4860
return;
4961
}
@@ -52,7 +64,7 @@ export default function useNoticeTimer(
5264
onEventUpdate(Math.min(passTimeRef.current / durationMs, 1));
5365

5466
if (!walking) {
55-
startTimestampRef.current = null;
67+
lastRafTimeRef.current = null;
5668
return;
5769
}
5870

@@ -62,6 +74,8 @@ export default function useNoticeTimer(
6274
return;
6375
}
6476

77+
lastRafTimeRef.current = Date.now();
78+
6579
const timeout = window.setTimeout(() => {
6680
passTimeRef.current = durationMs;
6781
onEventUpdate(1);
@@ -85,14 +99,15 @@ export default function useNoticeTimer(
8599
}
86100
}
87101

88-
startTimestampRef.current = Date.now();
89102
rafId = raf(step);
90103

91104
return () => {
92105
window.clearTimeout(timeout);
93106
raf.cancel(rafId);
94107
};
95-
}, [durationMs, walking]);
108+
// `useEvent` callbacks and refs always read latest values.
109+
// eslint-disable-next-line react-hooks/exhaustive-deps
110+
}, [durationMs, trackProgress, walking]);
96111

97112
return [onResume, onPause] as const;
98113
}

tests/index.test.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ describe('Notification.Basic', () => {
273273
it('continue timing after hover', () => {
274274
const { instance } = renderDemo({
275275
duration: 1,
276+
showProgress: true,
276277
});
277278

278279
act(() => {
@@ -291,10 +292,17 @@ describe('Notification.Basic', () => {
291292

292293
// Mouse in should not remove
293294
fireEvent.mouseEnter(document.querySelector('.rc-notification-notice'));
295+
const pausedProgress = document
296+
.querySelector<HTMLProgressElement>('.rc-notification-notice-progress')
297+
.getAttribute('value');
294298
act(() => {
299+
// Elapsed time should not advance while hovering.
295300
vi.advanceTimersByTime(1000);
296301
});
297302
expect(document.querySelector('.test')).toBeTruthy();
303+
expect(
304+
document.querySelector<HTMLProgressElement>('.rc-notification-notice-progress'),
305+
).toHaveAttribute('value', pausedProgress);
298306

299307
// Mouse out should not remove until 500ms later
300308
fireEvent.mouseLeave(document.querySelector('.rc-notification-notice'));

0 commit comments

Comments
 (0)