UTA DevHub
Guides

Offline Connectivity Management

Implementing network state monitoring and connectivity detection in React Native applications.

Offline Connectivity Management

Overview

This guide focuses on implementing robust connectivity monitoring in React Native applications.

  • Problem Solved: Ensures the application is aware of its online/offline status and can react accordingly.
  • When to Apply: Essential for any application that needs to function or provide feedback when network access is unavailable or unreliable.
  • Expected Outcomes: A reliable system for detecting network changes, providing this information to the application, and triggering appropriate actions (e.g., switching to offline mode, queueing data).

When To Use

  • Scenarios:
    • Applications that need to cache data for offline viewing.
    • Apps that allow users to perform actions offline that will be synced later.
    • Situations where providing immediate feedback about connection status improves user experience.
    • When needing to conserve battery or data by altering behavior based on connection type (e.g., Wi-Fi vs. cellular).
  • Prerequisites:
    • @react-native-community/netinfo library installed and configured.
    • A basic understanding of React Context API for state management.
  • Alternatives to Consider:
    • For very simple use cases, polling a known endpoint (less efficient).
    • Relying on individual API call failures (less proactive).

Architecture

This diagram illustrates how the NetInfo module interacts with the ConnectivityContext to provide status updates to application components.

Implementation Patterns

1. Connectivity Context

Create a React Context (ConnectivityContext) to manage and broadcast connectivity state. This context will hold isOnline, connectionType, and wasOfflineSinceLastCheck flags.

// src/core/offline/context/ConnectivityContext.tsx
import React, { createContext, useState, useEffect, useContext, useRef, ReactNode } from 'react';
import NetInfo, { NetInfoState, NetInfoStateType } from '@react-native-community/netinfo';
 
interface ConnectivityContextType {
  isOnline: boolean;
  wasOfflineSinceLastCheck: boolean; // True if transitioned from offline to online since last check
  connectionType: NetInfoStateType | null;
  connectionDetails: NetInfoState['details'] | null;
}
 
const initialState: ConnectivityContextType = {
  isOnline: true, // Assume online initially until first check
  wasOfflineSinceLastCheck: false,
  connectionType: null,
  connectionDetails: null,
};
 
const ConnectivityContext = createContext<ConnectivityContextType>(initialState);
 
export const ConnectivityProvider: React.FC<{children: ReactNode}> = ({ children }) => {
  const [isOnline, setIsOnline] = useState<boolean>(initialState.isOnline);
  const [wasOfflineSinceLastCheck, setWasOfflineSinceLastCheck] = useState<boolean>(initialState.wasOfflineSinceLastCheck);
  const [connectionType, setConnectionType] = useState<NetInfoStateType | null>(initialState.connectionType);
  const [connectionDetails, setConnectionDetails] = useState<NetInfoState['details'] | null>(initialState.connectionDetails);
 
  const previousIsOnline = useRef(isOnline); // Ref to track the previous online status
 
  useEffect(() => {
    const unsubscribe = NetInfo.addEventListener((state: NetInfoState) => {
      const currentOnlineStatus = !!state.isConnected && !!state.isInternetReachable;
 
      if (!previousIsOnline.current && currentOnlineStatus) {
        setWasOfflineSinceLastCheck(true); // Went from offline to online
      } else if (previousIsOnline.current && !currentOnlineStatus) {
        setWasOfflineSinceLastCheck(false); // Went from online to offline, reset flag
      } else if (currentOnlineStatus) {
        setWasOfflineSinceLastCheck(false); // Still online, reset flag
      }
      // If still offline, wasOfflineSinceLastCheck remains as is (potentially true if it was set by a quick blip)
 
      setIsOnline(currentOnlineStatus);
      setConnectionType(state.type);
      setConnectionDetails(state.details);
      previousIsOnline.current = currentOnlineStatus; // Update ref for next event
    });
 
    // Fetch initial state once
    NetInfo.fetch().then((state: NetInfoState) => {
      const initialOnlineStatus = !!state.isConnected && !!state.isInternetReachable;
      setIsOnline(initialOnlineStatus);
      setConnectionType(state.type);
      setConnectionDetails(state.details);
      previousIsOnline.current = initialOnlineStatus;
    });
 
    return () => unsubscribe(); // Cleanup listener on unmount
  }, []);
 
  return (
    <ConnectivityContext.Provider
      value={{ isOnline, wasOfflineSinceLastCheck, connectionType, connectionDetails }}
    >
      {children}
    </ConnectivityContext.Provider>
  );
};
 
export const useConnectivity = (): ConnectivityContextType => {
  const context = useContext(ConnectivityContext);
  if (context === undefined) {
    throw new Error('useConnectivity must be used within a ConnectivityProvider');
  }
  return context;
};

2. Application Setup

Wrap your root application component with the ConnectivityProvider.

// App.tsx
import React from 'react';
import { ConnectivityProvider } from '@/core/offline/context/ConnectivityContext';
// ... other imports for your app (Navigation, Redux Provider, etc.)
 
const App = () => {
  return (
    <ConnectivityProvider>
      {/* Your main app navigation and screens */}
    </ConnectivityProvider>
  );
};
 
export default App;

3. Using Connectivity Information

Consume the connectivity state in any component using the useConnectivity hook.

// src/components/MyNetworkAwareComponent.tsx
import React, { useEffect } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { useConnectivity } from '@/core/offline/context/ConnectivityContext';
 
const MyNetworkAwareComponent = () => {
  const { isOnline, connectionType, wasOfflineSinceLastCheck } = useConnectivity();
 
  useEffect(() => {
    if (wasOfflineSinceLastCheck) {
      console.log('Device was offline and is now back online. Triggering sync...');
      // Example: Trigger a data synchronization process
      // syncData();
    }
  }, [wasOfflineSinceLastCheck]);
 
  return (
    <View style={styles.container}>
      <Text style={isOnline ? styles.online : styles.offline}>
        Connection Status: {isOnline ? 'Online' : 'Offline'}
      </Text>
      {isOnline && connectionType && (
        <Text>Connection Type: {connectionType}</Text>
      )}
      {wasOfflineSinceLastCheck && isOnline && (
        <Text style={styles.syncMessage}>Syncing pending data...</Text>
      )}
    </View>
  );
};
 
const styles = StyleSheet.create({
  container: { padding: 10 },
  online: { color: 'green' },
  offline: { color: 'red' },
  syncMessage: { color: 'blue', marginTop: 5 },
});
 
export default MyNetworkAwareComponent;

Common Challenges

  • Fluctuating Signal: Network state can change rapidly.
    • Solution: Implement debouncing or a minimum "offline" duration before changing UI or behavior drastically. The wasOfflineSinceLastCheck flag helps manage actions upon returning online.
  • isInternetReachable Nuances: This flag can sometimes be unreliable on certain OS versions or network configurations.
    • Solution: For critical operations, supplement with a quick "ping" to a reliable server if isInternetReachable is true but operations still fail.
  • Initial State: NetInfo.fetch() provides an initial state, but a listener should also be set up immediately as the state can change between the fetch and listener registration.
    • Solution: The provided ConnectivityProvider handles this by fetching initial state and then relying on the event listener.

Performance Considerations

  • (Do ✅) Use the useConnectivity hook selectively in components that actually need to react to network changes.
  • (Do ✅) Memoize components that consume connectivity context if they are expensive to re-render, though the context value changes infrequently.
  • (Don't ❌) Trigger expensive operations (e.g., full data re-fetch) directly on every isOnline state change without further logic (like checking wasOfflineSinceLastCheck or specific needs).
  • (Consider 🤔) Implementing logic to adjust data fetching frequency based on connectionType (e.g., less frequent updates on cellular data vs. Wi-Fi).
  • (Be Aware ❗) The NetInfo event listener itself is lightweight, but frequent context updates could cause re-renders. React's reconciliation is generally efficient, but be mindful in complex UIs.

Best Practices (Do's and Don'ts)

Connection Validation

  • (Do ✅) Rely on isInternetReachable in conjunction with isConnected for a more accurate assessment of internet availability.
  • (Do ✅) For critical online-only actions, consider an application-level ping test to a trusted endpoint as a final confirmation if NetInfo reports online but actions fail.
  • (Don't ❌) Assume isConnected === true always means full internet access. It might only indicate a local network connection.
  • (Don't ❌) Trigger immediate data fetches or UI changes on every minor fluctuation without some form of debouncing or threshold if the connection is unstable.

State Management & UI Feedback

  • (Do ✅) Use the wasOfflineSinceLastCheck flag to trigger specific actions (like data sync) only when transitioning from an offline to an online state.
  • (Do ✅) Provide clear visual feedback to the user about the current connectivity status (e.g., a banner, an icon).
  • (Don't ❌) Block the UI unnecessarily when offline if the app can offer some functionality.
  • (Consider 🤔) Using a global state management system (like Redux or Zustand) in conjunction with the context if connectivity status needs to drive complex, app-wide state changes beyond simple UI updates.

Error Handling & Reliability

  • (Do ✅) Ensure the ConnectivityProvider is placed high in the component tree to be available globally.
  • (Do ✅) Handle the useContext for ConnectivityContext potentially returning undefined if not used within the provider (as shown in the hook implementation).
  • (Don't ❌) Assume NetInfo will always provide an immediate and perfectly accurate state, especially during rapid network changes or in unusual network environments.
  • (Be Aware ❗) The behavior and reliability of NetInfo can have slight variations between iOS and Android, and even between OS versions. Test thoroughly on target devices.

State Transitions Diagram

This diagram shows the possible states and transitions managed by the ConnectivityProvider.

Next Steps

Continue to the query-caching guide to learn how to persist data from your API (e.g., using TanStack Query) for offline access.