Skip to content

Commit fa3daa5

Browse files
authored
fix(useInterval): prevent immediate callback from refiring when enabled is toggled (toss#352)
1 parent 129c168 commit fa3daa5

3 files changed

Lines changed: 41 additions & 3 deletions

File tree

.changeset/lovely-spies-change.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'react-simplikit': patch
3+
---
4+
5+
fix(core/hooks): prevent immediate callback from re-firing when enabled is toggled

packages/core/src/hooks/useInterval/useInterval.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,26 @@ describe('useInterval', () => {
124124
});
125125
});
126126

127+
it('should not re-fire immediate callback when enabled is toggled', async () => {
128+
const callback = vi.fn();
129+
const { rerender } = await renderHookSSR(
130+
({ enabled }) =>
131+
useInterval(callback, {
132+
delay: 1000,
133+
immediate: true,
134+
enabled,
135+
}),
136+
{ initialProps: { enabled: true } }
137+
);
138+
139+
expect(callback).toHaveBeenCalledTimes(1);
140+
141+
rerender({ enabled: false });
142+
rerender({ enabled: true });
143+
144+
expect(callback).toHaveBeenCalledTimes(1);
145+
});
146+
127147
it('should handle enabled flag changes appropriately', async () => {
128148
const callback = vi.fn();
129149
const { rerender } = await renderHookSSR(

packages/core/src/hooks/useInterval/useInterval.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect } from 'react';
1+
import { useEffect, useRef } from 'react';
22

33
import { usePreservedCallback } from '../usePreservedCallback/index.ts';
44

@@ -45,12 +45,25 @@ export function useInterval(callback: () => void, options: IntervalOptions) {
4545
const enabled = typeof options === 'number' ? true : (options.enabled ?? true);
4646

4747
const preservedCallback = usePreservedCallback(callback);
48+
const immediateCalledRef = useRef(false);
4849

4950
useEffect(
5051
function runImmediateCallback() {
51-
if (immediate === true && enabled) {
52-
preservedCallback();
52+
if (immediate !== true) {
53+
immediateCalledRef.current = false;
54+
return;
55+
}
56+
57+
if (!enabled) {
58+
return;
5359
}
60+
61+
if (immediateCalledRef.current) {
62+
return;
63+
}
64+
65+
immediateCalledRef.current = true;
66+
preservedCallback();
5467
},
5568
[immediate, preservedCallback, enabled]
5669
);

0 commit comments

Comments
 (0)