Skip to content

fix(useInterval): clear a manually resumed interval on unmount - #208

Closed
ostapondo wants to merge 1 commit into
childrentime:mainfrom
ostapondo:fix/interval-manual-controls-unmount
Closed

fix(useInterval): clear a manually resumed interval on unmount#208
ostapondo wants to merge 1 commit into
childrentime:mainfrom
ostapondo:fix/interval-manual-controls-unmount

Conversation

@ostapondo

Copy link
Copy Markdown
Contributor

Description

useInterval never clears its timer on unmount when controls: true is used.

The effect that owns the cleanup bails out before it can register one:

if (controls) {
  return
}

So an interval started through resume() keeps firing after the component is
gone. The hook's docs promise the opposite:

The interval is automatically cleared on component unmount.

To reproduce: mount with { controls: true }, call resume(), unmount, then
advance the timers. The callback keeps running — 10 more calls over 500ms at a
50ms delay in the test I added.

I put the unmount cleanup in its own mount-scoped effect rather than returning
clean from the existing one. That keeps the current delay-change behaviour
untouched: in manual mode, updating delay still doesn't stop a running timer,
which felt like a separate decision from fixing the leak.

While I was in there — resume() overwrote timer.current without clearing the
previous timer, so calling it twice orphaned the first interval and neither
pause() nor the unmount cleanup could reach it afterwards. It now clears first.

Type of Change

  • Bug fix
  • New hook
  • Enhancement to existing hook
  • Documentation update
  • Other (please describe)

Checklist

  • I have read the Contributing Guide
  • My code follows the project's coding style
  • I have added tests for my changes
  • All existing tests pass
  • I have updated the documentation

No doc change: the existing page already describes the fixed behaviour.

Two tests added to useInterval/index.spec.ts — one for the unmount leak, one
for the double-resume() orphan. Both fail on main and pass here; the other 9
in that file are untouched.

@ostapondo
ostapondo force-pushed the fix/interval-manual-controls-unmount branch 2 times, most recently from 2e436b1 to ead6514 Compare July 29, 2026 13:49
With `controls: true` the effect returns before it registers a cleanup,
so an interval started through resume() keeps firing after the component
unmounts - the hook docs already state the interval is cleared on
unmount. Register the cleanup in its own mount-scoped effect so it runs
in both modes without changing what happens when `delay` updates.

resume() also overwrote timer.current without clearing the previous
timer, so calling it twice left an interval that neither pause() nor the
unmount cleanup could reach. Clear before starting a new one.
@ostapondo
ostapondo force-pushed the fix/interval-manual-controls-unmount branch from ead6514 to f35d6a8 Compare July 29, 2026 13:51
@ostapondo ostapondo closed this by deleting the head repository Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant