Offline Support
Implement offline support and data synchronization for React Native applications. This is a foundational concept in cross-platform mobile development that professional developers rely on daily. The explanations below are written to be beginner-friendly while covering the depth and nuance that comes from real-world React Native experience. Take your time with each section and practice the examples
Offline Data Storage
Use AsyncStorage, SQLite, or Realm to store data locally and provide offline functionality to users.. This is an essential concept that every React Native developer must understand thoroughly. In professional development environments, getting this right can mean the difference between code that works reliably and code that breaks in production. The following sections break this down into clear, digestible pieces with practical examples you can try immediately
Data Synchronization
Implement data synchronization strategies to sync local data with server when connectivity is restored.. This is an essential concept that every React Native developer must understand thoroughly. In professional development environments, getting this right can mean the difference between code that works reliably and code that breaks in production. The following sections break this down into clear, digestible pieces with practical examples you can try immediately
Offline Support Implementation
// services/offlineManager.js
import AsyncStorage from '@react-native-async-storage/async-storage';
import NetInfo from '@react-native-community/netinfo';
class OfflineManager {
constructor() {
this.isOnline = true;
this.pendingActions = [];
this.syncQueue = [];
this.setupNetworkListener();
}
setupNetworkListener() {
NetInfo.addEventListener(state => {
const wasOffline = !this.isOnline;
this.isOnline = state.isConnected;
if (wasOffline && this.isOnline) {
this.syncPendingData();
}
});
}
// Store data locally
async storeData(key, data) {
try {
const jsonData = JSON.stringify({
data,
timestamp: Date.now(),
synced: false,
});
await AsyncStorage.setItem(key, jsonData);
if (this.isOnline) {
this.syncToServer(key, data);
} else {
this.addToSyncQueue(key, data);
}
} catch (error) {
console.error('Error storing data:', error);
}
}
// Retrieve data from local storage
async getData(key) {
try {
const jsonData = await AsyncStorage.getItem(key);
if (jsonData) {
const parsed = JSON.parse(jsonData);
return parsed.data;
}
return null;
} catch (error) {
console.error('Error retrieving data:', error);
return null;
}
}
// Add to sync queue when offline
addToSyncQueue(key, data) {
this.syncQueue.push({ key, data, timestamp: Date.now() });
}
// Sync pending data when online
async syncPendingData() {
console.log('Syncing pending data...');
for (const item of this.syncQueue) {
try {
await this.syncToServer(item.key, item.data);
await this.markAsSynced(item.key);
} catch (error) {
console.error('Error syncing item:', error);
}
}
this.syncQueue = [];
}
// Sync data to server
async syncToServer(key, data) {
try {
const response = await fetch(`https://api.yourapp.com/sync/${key}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (response.ok) {
await this.markAsSynced(key);
} else {
throw new Error('Sync failed');
}
} catch (error) {
console.error('Error syncing to server:', error);
throw error;
}
}
// Mark data as synced
async markAsSynced(key) {
try {
const jsonData = await AsyncStorage.getItem(key);
if (jsonData) {
const parsed = JSON.parse(jsonData);
parsed.synced = true;
await AsyncStorage.setItem(key, JSON.stringify(parsed));
}
} catch (error) {
console.error('Error marking as synced:', error);
}
}
// Get offline status
isOffline() {
return !this.isOnline;
}
// Get pending sync count
getPendingSyncCount() {
return this.syncQueue.length;
}
}
// Offline-aware API service
class OfflineAPI {
constructor() {
this.offlineManager = new OfflineManager();
}
async get(endpoint) {
const cacheKey = `cache_${endpoint}`;
if (this.offlineManager.isOffline()) {
return await this.offlineManager.getData(cacheKey);
}
try {
const response = await fetch(`https://api.yourapp.com${endpoint}`);
const data = await response.json();
// Cache the data
await this.offlineManager.storeData(cacheKey, data);
return data;
} catch (error) {
// Return cached data if available
const cachedData = await this.offlineManager.getData(cacheKey);
if (cachedData) {
return cachedData;
}
throw error;
}
}
async post(endpoint, data) {
if (this.offlineManager.isOffline()) {
// Store for later sync
await this.offlineManager.storeData(`pending_${Date.now()}`, {
endpoint,
method: 'POST',
data,
});
return { success: true, offline: true };
}
try {
const response = await fetch(`https://api.yourapp.com${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
return await response.json();
} catch (error) {
// Store for later sync
await this.offlineManager.storeData(`pending_${Date.now()}`, {
endpoint,
method: 'POST',
data,
});
throw error;
}
}
}
// Offline indicator component
const OfflineIndicator = () => {
const [isOffline, setIsOffline] = useState(false);
const [pendingSync, setPendingSync] = useState(0);
useEffect(() => {
const unsubscribe = NetInfo.addEventListener(state => {
setIsOffline(!state.isConnected);
});
return unsubscribe;
}, []);
if (!isOffline) return null;
return (
<View style={styles.offlineIndicator}>
<Text style={styles.offlineText}>
You're offline. Changes will sync when you're back online.
</Text>
{pendingSync > 0 && (
<Text style={styles.syncText}>
{pendingSync} items pending sync
</Text>
)}
</View>
);
};
const styles = {
offlineIndicator: {
backgroundColor: '#ff9800',
padding: 10,
alignItems: 'center',
},
offlineText: {
color: 'white',
fontWeight: 'bold',
},
syncText: {
color: 'white',
fontSize: 12,
marginTop: 2,
},
};
export { OfflineManager, OfflineAPI, OfflineIndicator };