useSmartPolling: toggling `enabled` off leaves isPolling=true, and toggling it back on never restarts polling

Open
#570 3 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@Proxima84-code is already working on this.

Since Oct 6, 2026.

  • #578 by @PINYOPATTANAWASANPORN — open
  • #582 by @PINYOPATTANAWASANPORN — open
  • #585 by @Proxima84-code — open

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
25/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Stale
Tech stack
react, typescript
Domain
frontend

Research direction

Start with the useSmartPolling hook and how useBountyStatus forwards enabled to it, focusing on the main effect, the hasInitialized guard, and the cleanup that skips dispatching STOP_POLLING. Pull request #585 is already open against this issue, so check it before writing code. Done means enabled going false sets isPolling to false, going true again restarts polling, and fake-timer tests cover both transitions.

Written by the indexing model from the issue text.

Description

bug help wanted very hard

Problem

useSmartPolling accepts enabled, which useBountyStatus forwards, but the main effect doesn't handle it changing:

  • true → false: the cleanup clears the interval, but deliberately doesn't dispatch STOP_POLLING ("to avoid state updates during unmount"). The body then returns early because !enabled. So isPolling stays true even though nothing is polling, and useBountyStatus's isLive keeps reporting live.
  • false → true: if the hook started enabled, hasInitialized.current is already true, so the if (!hasInitialized.current) block is skipped and polling never resumes. Only a tab hide/show, through the visibilitychange handler, brings it back.

The cleanup can't tell a real unmount apart from a dependency change, so the "don't dispatch on unmount" rule also swallows the legitimate "polling stopped" transition.

Expected

  • enabled=false stops the timer and sets isPolling=false.
  • enabled=true again, after a disable, starts polling immediately.

Suggested fix

Handle enable and disable explicitly:

  • use a dedicated effect on enabled that starts or stops the timer and dispatches the matching action,
  • and keep the unmount-only guard in a separate useEffect(() => () => { isMountedRef.current = false }, []), which is the pattern useClaimRace uses (#361).

Add tests for disabling and re-enabling with fake timers.

Related: #46, #359, and the separate issue about polling dying after the first re-render. That one shares the hasInitialized root cause but is triggered by callback identity, not enabled.

Dominant language
TypeScript
Stars
1
Forks
94
Avg merge
1h 2m
Merged PRs (30d)
25

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from MergeFi/frontend

All issues in MergeFi/frontend

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.