Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/framework/react/react-native.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,14 @@ import { onlineManager } from '@tanstack/react-query'
import * as Network from 'expo-network'

onlineManager.setEventListener((setOnline) => {
Network.getNetworkStateAsync().then((state) => {
setOnline(!!state.isConnected)
})

const eventSubscription = Network.addNetworkStateListener((state) => {
setOnline(!!state.isConnected)
})

return eventSubscription.remove
})
Comment on lines 43 to 53
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Race condition: async fetch result can overwrite a more recent listener-fired state

Because getNetworkStateAsync() is asynchronous, the following sequence is possible:

  1. getNetworkStateAsync() is invoked β€” captures state at Tβ‚€ (e.g., offline).
  2. addNetworkStateListener is registered synchronously.
  3. A state change fires the listener at T₁ β†’ setOnline(true).
  4. The async call resolves at Tβ‚‚ with the stale Tβ‚€ snapshot β†’ setOnline(false), silently overwriting the correct current state.

A guard flag prevents the async snapshot from clobbering a fresher listener result:

πŸ› Proposed fix with race-safe initialization guard + error handling
 onlineManager.setEventListener((setOnline) => {
-  Network.getNetworkStateAsync().then((state) => {
-    setOnline(!!state.isConnected)
-  })
-
-  const eventSubscription = Network.addNetworkStateListener((state) => {
+  let initialised = false
+
+  const eventSubscription = Network.addNetworkStateListener((state) => {
+    initialised = true
     setOnline(!!state.isConnected)
   })
-  
+
+  Network.getNetworkStateAsync()
+    .then((state) => {
+      if (!initialised) {
+        setOnline(!!state.isConnected)
+      }
+    })
+    .catch(() => {
+      // getNetworkStateAsync can reject on some platforms/SDK versions
+    })
+
   return eventSubscription.remove
 })

Additionally, the bare .then() with no .catch() means that if getNetworkStateAsync() rejects (e.g., the native module is unavailable, which surfaces as an unhandled promise rejection), the error is silently swallowed and will appear as an unhandled rejection in the app. The .catch() in the proposed fix addresses both concerns.

πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onlineManager.setEventListener((setOnline) => {
Network.getNetworkStateAsync().then((state) => {
setOnline(!!state.isConnected)
})
const eventSubscription = Network.addNetworkStateListener((state) => {
setOnline(!!state.isConnected)
})
return eventSubscription.remove
})
onlineManager.setEventListener((setOnline) => {
let initialised = false
const eventSubscription = Network.addNetworkStateListener((state) => {
initialised = true
setOnline(!!state.isConnected)
})
Network.getNetworkStateAsync()
.then((state) => {
if (!initialised) {
setOnline(!!state.isConnected)
}
})
.catch(() => {
// getNetworkStateAsync can reject on some platforms/SDK versions
})
return eventSubscription.remove
})
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/react/react-native.md` around lines 43 - 53, The async init in
onlineManager.setEventListener can be overwritten by a later listener event and
lacks error handling: introduce a local guard flag (e.g., "initialResultIgnored"
or "stale") inside the setEventListener callback that is flipped when the
synchronous addNetworkStateListener fires (or when its callback runs) so the
Promise result from Network.getNetworkStateAsync() only calls setOnline if the
listener hasn't already provided a newer state; also attach .catch(...) to
getNetworkStateAsync() to log/handle errors, and ensure you still return
eventSubscription.remove from the setEventListener callback (use the same
identifiers: Network.getNetworkStateAsync, Network.addNetworkStateListener,
setOnline, eventSubscription.remove).

```
Expand Down