diff --git a/src/Notification.tsx b/src/Notification.tsx index d7c5c286..395a8f05 100644 --- a/src/Notification.tsx +++ b/src/Notification.tsx @@ -95,6 +95,7 @@ const Notification = React.forwardRef((props, // Behavior duration = 4.5, showProgress, + times, hovering: forcedHovering, pauseOnHover = true, @@ -118,7 +119,7 @@ const Notification = React.forwardRef((props, // ======================== Duration ======================== const [hovering, setHovering] = React.useState(false); - const [onResume, onPause] = useNoticeTimer(duration, onInternalClose, setPercent); + const [onResume, onPause] = useNoticeTimer(duration, times, onInternalClose, setPercent); const validPercent = 100 - Math.min(Math.max(percent * 100, 0), 100); const Progress = components?.progress || DefaultProgress; diff --git a/src/hooks/useNoticeTimer.ts b/src/hooks/useNoticeTimer.ts index dafc03ea..1387bdc3 100644 --- a/src/hooks/useNoticeTimer.ts +++ b/src/hooks/useNoticeTimer.ts @@ -7,6 +7,7 @@ import { raf, useEvent } from '@rc-component/util'; */ export default function useNoticeTimer( duration: number | false | null, + times: number | undefined, onClose: VoidFunction, onUpdate: (ptg: number) => void, ) { @@ -16,6 +17,8 @@ export default function useNoticeTimer( const onEventUpdate = useEvent(onUpdate); const [walking, setWalking] = React.useState(durationMs > 0); + const walkingRef = React.useRef(walking); + walkingRef.current = walking; const passTimeRef = React.useRef(0); const lastRafTimeRef = React.useRef(null); @@ -42,14 +45,22 @@ export default function useNoticeTimer( } else { onEventUpdate(0); } - }, [durationMs]); + }, [durationMs, onEventUpdate]); // Reset when durationMs changed. React.useEffect(() => { passTimeRef.current = 0; + lastRafTimeRef.current = null; setWalking(durationMs > 0); }, [durationMs]); + // Restart the timer when an existing notice is updated with the same key. + React.useEffect(() => { + passTimeRef.current = 0; + lastRafTimeRef.current = walkingRef.current ? Date.now() : null; + onEventUpdate(0); + }, [times, onEventUpdate]); + // Trigger update when walking changed. React.useEffect(() => { if (!walking) { @@ -75,7 +86,7 @@ export default function useNoticeTimer( return () => { raf.cancel(rafId!); }; - }, [durationMs, walking]); + }, [durationMs, times, walking, onEventClose, onEventUpdate]); return [onResume, onPause] as const; } diff --git a/tests/index.test.tsx b/tests/index.test.tsx index ac34c84b..76eafd77 100644 --- a/tests/index.test.tsx +++ b/tests/index.test.tsx @@ -64,6 +64,42 @@ describe('Notification.Basic', () => { unmount(); }); + it('resets duration when updating a notification with the same key', () => { + const { instance } = renderDemo(); + + act(() => { + instance.open({ + key: 'update', + description:

first

, + duration: 1, + }); + }); + + act(() => { + step(800); + }); + + act(() => { + instance.open({ + key: 'update', + description:

second

, + duration: 1, + }); + }); + + act(() => { + step(300); + }); + + expect(document.querySelector('.test')).toHaveTextContent('second'); + + act(() => { + step(700); + }); + + expect(document.querySelector('.test')).toBeFalsy(); + }); + it('works with custom close icon', () => { const { instance } = renderDemo();