ReactNativeOnlineDetector sometimes never fires after offline -> online transition
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 3.9k
- Forks
- 266
- Avg merge
- 1d 4h
- Merged PRs (30d)
- 55
Description
- I've validated the bug against the latest version of DB packages
Describe the bug
ReactNativeOnlineDetector sometimes never calls notifyListeners on the offline->online transition. When this happens, the offline executor stops retrying pending transactions until the app is foregrounded (which triggers notifyListeners via the AppState path). The app otherwise appears healthy, the outbox has the transaction, the network is back, but no sync attempt is ever made.
To Reproduce
- Run a React Native app (Expo dev client) that uses
startOfflineExecutorfrom@tanstack/offline-transactions/react-native. Reach a real remote server (I tested over Tailscale). - With the app in the foreground and online, enable airplane mode.
- Create a pending transaction via
createOfflineAction(...). Confirm the outbox contains 1 item (e.g. viaexecutor.getPendingCount()). - Disable airplane mode. Network is back within a few seconds.
- Wait. The executor never retries, no
[Sync]logs, no fetch attempts, pending count stays at 1 indefinitely. - Background the app and foreground it again. Sync runs immediately.
Expected behavior
After the device returns online, the executor should retry the pending transaction within a short window — the same way foregrounding the app currently causes it to retry.
Smartphone:
- Device: Google Pixel 9
- OS: GrapheneOS
- App runtime: Expo SDK 55, React Native 0.83.4,
@react-native-community/netinfo11.5.2 - @tanstack/offline-transactions: 1.0.30 , @tanstack/db: 0.6.5
Additional context
Looking at packages/offline-transactions/src/connectivity/ReactNativeOnlineDetector.ts, two things seem worth looking at together:
- Edge detection via
!this.wasConnected: if the cache drifts out of sync with reality, the edge check silently fails forever. startListening()has an asyncNetInfo.fetch().then(state => this.wasConnected = ...)running alongside the synchronousaddEventListenerhandler, which also writeswasConnected. Iffetch()resolves after one or more events have already fired, it can overwrite a correctwasConnectedwith a stale one.
Empirically on our device, after airplane-mode toggling, wasConnected ends up stuck at true, so the subsequent online event evaluates as true && !true → no notifyListeners. AppState → active fires notifyListeners directly without consulting wasConnected, which is why backgrounding+foregrounding recovers.
For comparison, WebOnlineDetector in the same package has no edge detection and no cached state. It calls notifyListeners() on every window.online event and answers isOnline() by reading navigator.onLine live.
Workaround
Passing a custom OnlineDetector to startOfflineExecutor that notifies on every online NetInfo event (mirroring the web detector's behavior) eliminates the issue.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with packages/offline-transactions/src/connectivity/ReactNativeOnlineDetector.ts, tracing startListening(), NetInfo.fetch(), the event handler, and the wasConnected cache. Reproduce the airplane-mode transition described in the issue and compare it with WebOnlineDetector; done means an online transition reliably calls notifyListeners so the pending executor retries without foregrounding the app.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react-native, typescript
- Domain
- mobile-dev
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100