From 0dfdebe1b9ca8a9898631d4c252a49bc3c097a60 Mon Sep 17 00:00:00 2001 From: AMin Date: Sun, 25 Oct 2026 13:37:59 +0800 Subject: [PATCH 1/2] fix(useRafLoop): avoid duplicate frames after restarting --- src/useRafLoop.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/useRafLoop.ts b/src/useRafLoop.ts index 23a0807478..81fa7471d6 100644 --- a/src/useRafLoop.ts +++ b/src/useRafLoop.ts @@ -13,8 +13,8 @@ export default function useRafLoop( const step = useCallback((time: number) => { if (rafActivity.current) { - rafCallback.current(time); raf.current = requestAnimationFrame(step); + rafCallback.current(time); } }, []); From f8349ad1fae8937f0cb873995dcf8b800db7c897 Mon Sep 17 00:00:00 2001 From: AMin Date: Sun, 25 Oct 2026 13:38:32 +0800 Subject: [PATCH 2/2] test(useRafLoop): cover restarting from the callback --- tests/useRafLoop.test.tsx | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/useRafLoop.test.tsx b/tests/useRafLoop.test.tsx index 3830f88ac3..1199de53db 100644 --- a/tests/useRafLoop.test.tsx +++ b/tests/useRafLoop.test.tsx @@ -93,6 +93,40 @@ describe('useRafLoop', () => { }); describe('returned methods', () => { + it('should keep one loop when restarting from the callback', () => { + const spy = jest.fn(() => { + if (spy.mock.calls.length === 1) { + hook.result.current[0](); + hook.result.current[1](); + } + }); + const hook = renderHook(() => useRafLoop(spy)); + + requestAnimationFrame.step(); + expect(spy).toBeCalledTimes(1); + requestAnimationFrame.step(3); + expect(spy).toBeCalledTimes(4); + + hook.result.current[0](); + requestAnimationFrame.step(); + expect(spy).toBeCalledTimes(4); + }); + + it('should cancel the pending frame when stopped from the callback', () => { + const spy = jest.fn(() => { + if (spy.mock.calls.length === 1) { + hook.result.current[0](); + } + }); + const hook = renderHook(() => useRafLoop(spy)); + + requestAnimationFrame.step(); + expect(hook.result.current[2]()).toBe(false); + hook.result.current[1](); + requestAnimationFrame.step(3); + expect(spy).toBeCalledTimes(4); + }); + it('stop method should stop the loop', () => { const spy = jest.fn(); const hook = renderHook(() => useRafLoop(spy), { initialProps: false });