skip to content

Online Status Detection

NetInfo and expo-network report whether the phone has a network and of which type, but 'connected' is not 'the API answers'. Interviewers ask how an app decides it is really offline.

part ofReact Nativeoverview, primer and where to startread it →
on this pageshow

explore

questions

5

In React Native, how do you read the network state once and subscribe to its changes with NetInfo's fetch, addEventListener and useNetInfo?

level: juniorimportance: must knowfreq 45%

answer

  1. one-off promise vs subscription vs hook
  2. fetch may return the cached state
  3. refresh forces a new native query
  4. addEventListener returns a function
  5. hook starts as unknown with nulls

basics

~20 s

NetInfo.fetch() resolves to the current NetInfoState (the cached latest one if available; refresh() forces a new query). NetInfo.addEventListener(cb) calls back now and on every change and returns an unsubscribe function. useNetInfo() wraps that subscription in a hook that starts as unknown.

solid answer

~40 s

`NetInfo.fetch()` returns a promise of a `NetInfoState`; if the library already holds a latest state it resolves to that, and `NetInfo.refresh()` forces a fresh native query. For ongoing updates, `NetInfo.addEventListener(listener)` calls the listener soon after subscribing with the latest state and then on each change, and returns an **unsubscribe function** that you call on cleanup. In components, `useNetInfo()` does that subscription for you and returns the state; its first render is `type: 'unknown'` with `isConnected` and `isInternetReachable` set to `null`, so the UI must handle unknown. All three share one global singleton. I use `fetch` for a one-off decision, such as before starting a large sync, `addEventListener` in non-React code like a sync service, and `useNetInfo` for UI like the field-inspection banner.

code

typescript · 19 lines
typescript
import NetInfo from '@react-native-community/netinfo';

export function startSyncWatcher(onBackOnline: () => void) {
  let wasReachable: boolean | null = null;
  const unsubscribe = NetInfo.addEventListener((state) => {
    if (wasReachable === false && state.isInternetReachable === true) {
      onBackOnline();
    }
    if (state.isInternetReachable !== null) {
      wasReachable = state.isInternetReachable;
    }
  });
  return unsubscribe; // a plain function, call it to stop listening
}

export async function canStartBulkSync(): Promise<boolean> {
  const state = await NetInfo.fetch();
  return state.isInternetReachable !== false;
}

go deeper

for a junior

Recall the three entry points: fetch for a one-off check, addEventListener for updates with its unsubscribe function, and useNetInfo in components.

for a middle

Explain the singleton, that fetch may resolve cached state while refresh forces a query, and that useNetInfo starts as unknown with nulls.

for a senior

Keep configuration in one startup call, avoid the useNetInfo(configuration) teardown trap, and use useNetInfoInstance when a screen needs isolated settings.

for a principal

Decide where network state lives in the app: one service feeding stores and sync, rather than scattered hooks each reacting differently to the same transitions.

## The library and its singleton Network status in React Native comes from the community library `@react-native-community/netinfo` (core React Native has no NetInfo module). It keeps **one global state manager** that listens to the native module and, where needed, runs internet-reachability checks. Its main entry points all read from that singleton: | API | Returns | Use it for | |---|---|---| | `NetInfo.fetch()` | `Promise<NetInfoState>` | A one-off check at a decision point | | `NetInfo.refresh()` | `Promise<NetInfoState>` | Forcing a fresh native query | | `NetInfo.addEventListener(fn)` | an unsubscribe **function** | Services and non-React code | | `useNetInfo()` | `NetInfoState` | Components that render network status | ## fetch and refresh `NetInfo.fetch()` resolves with the **latest state the library already knows** when one exists; otherwise it queries native code. That is fast, but it can be slightly behind reality right after a change. `NetInfo.refresh()` always asks native code again, and concurrent calls share one in-flight request. `fetch` also accepts an optional interface name to query a specific interface such as Wi-Fi. In the field-inspection app, a reasonable use is checking the state once before starting a bulk photo sync: 1. `const state = await NetInfo.fetch();` 2. If `state.isInternetReachable === false`, skip the sync and keep the queue. 3. Otherwise start, and let request failures handle the rest. ## addEventListener `NetInfo.addEventListener(listener)` registers a callback that receives a `NetInfoState`: - It is called **soon after subscribing** with the latest state, so you do not need a separate `fetch` to initialise. - It is then called **whenever the state changes**; the library's docs warn not to assume identical timing across devices or platforms. - It returns a **function**, not an object with `remove()`. Calling that function unsubscribes. Forgetting to call the unsubscribe function leaks the listener and keeps a closure (and whatever it references) alive for the app's lifetime. ## useNetInfo `useNetInfo()` subscribes in an effect, unsubscribes on unmount and returns the current state. Its **initial state** is: - `type: 'unknown'` - `isConnected: null` - `isInternetReachable: null` - `details: null` So the first render always looks unknown, and components must not interpret that as offline. Two refinements: - **Configuration.** `useNetInfo(configuration)` calls `NetInfo.configure` on every render, and `configure` tears down the singleton and stops previously added listeners. Configure once at app start instead. - **Isolated instances.** `useNetInfoInstance(isPaused, configuration)` runs a separate state manager for one component, with its own configuration and a `refresh` function, without touching the global singleton. ## Testing code that uses NetInfo NetInfo is a native module, so Jest tests need its mock. The library ships one at `@react-native-community/netinfo/jest/netinfo-mock.js`, which you register from your Jest setup file. With the mock in place, tests can drive the listener or the hook with hand-built states, for example a transition from `isInternetReachable: false` to `true`, and assert that the sync service reacts exactly once. ## Where each API belongs in an app A common structure for the field-inspection app: 1. **A connectivity service**, started once at launch, subscribes with `NetInfo.addEventListener`, keeps the latest state and notifies the sync queue on transitions from unreachable to reachable. 2. **UI components** such as the offline banner call `useNetInfo()` and only render. 3. **Decision points**, such as "start bulk sync now?" or a Retry button, call `NetInfo.fetch()`, or `NetInfo.refresh()` right after a user action where freshness matters. This keeps side effects out of components and gives one place to log transitions for debugging. It also means only one listener drives syncing; if several components each started uploads from their own hook, a single reconnect could trigger duplicate work. On cleanup, services call the function returned by `addEventListener`, and hooks clean up on their own when the component unmounts. ## Reading the state object Beyond the two booleans, a state has `type` (`'wifi'`, `'cellular'`, `'none'`, `'unknown'` and others) and `details`, which is `null` when disconnected and otherwise holds type-specific fields such as `isConnectionExpensive` and, for cellular, `cellularGeneration`. ## Interview checklist Name the three APIs and when each fits, mention that `fetch` may return cached data while `refresh` forces a query, show the **unsubscribe function** from `addEventListener`, and point out that `useNetInfo` starts unknown.

  • A teammate writes const sub = NetInfo.addEventListener(cb); and later calls sub.remove(). What happens?
    It throws, because NetInfo's `addEventListener` returns a plain unsubscribe function, not a subscription object. Call `sub()` instead. This differs from React Native core APIs such as `AppState.addEventListener`, which return an object with `remove()`.
  • When would you call NetInfo.refresh() instead of fetch()?
    When a decision depends on the state right now, for example immediately after the user taps Retry, because `fetch` can resolve with the cached latest state. `refresh` forces a new native query, and parallel calls share one request.

saying these in an interview costs you the question

  • NetInfo is imported from 'react-native' in current versions.
  • NetInfo.fetch() always performs a fresh native query.
  • addEventListener returns a subscription object with a remove() method.
  • useNetInfo's first render already contains the real network state.
  • Passing a configuration to useNetInfo on every render is harmless.
open as a page

With React Native's NetInfo, what is the difference between isConnected and isInternetReachable, and which should drive an offline banner?

level: middleimportance: must knowfreq 55%

basics

~20 s

isConnected says the device has an active network link; isInternetReachable says that link actually reaches the internet, and is null while unknown. Drive an offline banner from isInternetReachable === false, never from null, and still treat failed API calls as the final word.

open as a page

In an Expo app, how does expo-network's useNetworkState differ from NetInfo's useNetInfo, and when is it not enough?

level: middleimportance: should knowfreq 24%

basics

~20 s

expo-network's useNetworkState returns { type, isConnected, isInternetReachable }, starting as an empty object. On Android reachability comes from the OS's validated-network check, but on iOS isInternetReachable always equals isConnected, and there is no probe to configure; NetInfo covers those gaps.

open as a page

In React Native NetInfo, what do reachabilityUrl and the other reachability options in NetInfo.configure control, and why point them at your own API?

level: seniorimportance: should knowfreq 22%

basics

~20 s

They configure NetInfo's own HTTP probe: reachabilityUrl, method and headers, a reachabilityTest on the response, polling intervals and a request timeout. Pointing it at your API's health endpoint makes 'reachable' mean 'our backend answers'. Call NetInfo.configure once at startup.

open as a page

How can a React Native app use NetInfo's type and details.isConnectionExpensive to postpone large photo uploads on metered networks?

level: middleimportance: nice to knowfreq 20%

basics

~20 s

NetInfo's type says wifi, cellular or none, and details.isConnectionExpensive flags metered connections: on Android the OS's metered flag, on iOS true for cellular. Start bulk uploads only when reachable and not expensive, and re-check on each state change.

open as a page