React Native:API响应SQLite存储及离线交易状态同步实现问询
Got it, let's walk through building this React Native app with offline support step by step. I'll cover every part from setting up local storage to handling syncing when the network comes back.
You'll need a few key libraries to make this work:
expo-sqlite(orreact-native-sqlite-storageif you're not using Expo) for local SQLite storage@react-native-community/netinfoto detect network connectivityaxios(or native fetch) for API calls
Install them with:
# If using Expo npx expo install expo-sqlite @react-native-community/netinfo axios # For bare React Native npm install react-native-sqlite-storage @react-native-community/netinfo axios
First, create a table to store transaction data. We'll add an isSynced flag to track if local changes have been sent to the server:
// db.js import * as SQLite from 'expo-sqlite'; const db = SQLite.openDatabase('transactions.db'); export const initDatabase = () => { return new Promise((resolve, reject) => { db.transaction(tx => { tx.executeSql( `CREATE TABLE IF NOT EXISTS transactions ( id INTEGER PRIMARY KEY NOT NULL, amount TEXT NOT NULL, date TEXT NOT NULL, status TEXT NOT NULL, isSynced INTEGER DEFAULT 1 );`, [], () => resolve(), (_, error) => reject(error) ); }); }); };
The isSynced field uses 1 for synced (default) and 0 for pending changes that need to be sent to the server.
On app launch, check if there's a network connection. If yes, pull data from the server and save it locally. If not, load directly from the database:
// TransactionService.js import axios from 'axios'; import { db } from './db'; import NetInfo from '@react-native-community/netinfo'; export const loadTransactions = async () => { const networkState = await NetInfo.fetch(); if (networkState.isConnected) { // Fetch from server const response = await axios.get('https://your-api.com/transactions'); const serverTransactions = response.data; // Save to local DB (replace existing to keep data up to date) await saveTransactionsToDB(serverTransactions); } // Load from local DB regardless of network return getTransactionsFromDB(); }; const saveTransactionsToDB = (transactions) => { return new Promise((resolve, reject) => { db.transaction(tx => { transactions.forEach(transaction => { tx.executeSql( `INSERT OR REPLACE INTO transactions (id, amount, date, status) VALUES (?, ?, ?, ?)`, [transaction.id, transaction.amount, transaction.date, transaction.status], null, (_, error) => console.error('Error saving transaction:', error) ); }); }, reject, resolve); }); }; const getTransactionsFromDB = () => { return new Promise((resolve, reject) => { db.transaction(tx => { tx.executeSql( 'SELECT * FROM transactions ORDER BY date DESC', [], (_, { rows }) => resolve(rows._array), (_, error) => reject(error) ); }); }); };
When a user modifies a transaction status, first update the local database and mark it as unsynced. This ensures the change is visible immediately even without network:
// TransactionService.js export const updateTransactionStatus = async (transactionId, newStatus) => { return new Promise((resolve, reject) => { db.transaction(tx => { tx.executeSql( `UPDATE transactions SET status = ?, isSynced = 0 WHERE id = ?`, [newStatus, transactionId], (_, result) => resolve(result), (_, error) => reject(error) ); }); }); };
In your component, call this function when the user selects a new status, then refresh the local list:
// TransactionList.js import { useState, useEffect } from 'react'; import { View, Text, Button, FlatList, StyleSheet } from 'react-native'; import { loadTransactions, updateTransactionStatus } from './TransactionService'; const TransactionList = () => { const [transactions, setTransactions] = useState([]); useEffect(() => { loadTransactions().then(setTransactions); }, []); const handleStatusChange = async (id, newStatus) => { await updateTransactionStatus(id, newStatus); // Refresh the list after local update const updatedTransactions = await loadTransactions(); setTransactions(updatedTransactions); }; return ( <FlatList data={transactions} keyExtractor={item => item.id.toString()} renderItem={({ item }) => ( <View style={styles.transactionItem}> <Text style={styles.amountText}>Amount: ${item.amount}</Text> <Text style={styles.statusText}>Status: {item.status}</Text> <View style={styles.buttonGroup}> <Button title="Mark Completed" onPress={() => handleStatusChange(item.id, 'completed')} color="#4CAF50" /> <Button title="Mark Pending" onPress={() => handleStatusChange(item.id, 'pending')} color="#FFC107" /> </View> </View> )} /> ); }; const styles = StyleSheet.create({ transactionItem: { padding: 16, borderBottomWidth: 1, borderBottomColor: '#eee' }, amountText: { fontSize: 16, marginBottom: 8 }, statusText: { fontSize: 14, color: '#666', marginBottom: 12 }, buttonGroup: { flexDirection: 'row', gap: 10 } }); export default TransactionList;
Set up a network listener to detect when connectivity is restored, then sync all unsynced changes to the server:
// SyncService.js import NetInfo from '@react-native-community/netinfo'; import axios from 'axios'; import { db } from './db'; export const setupSyncListener = () => { NetInfo.addEventListener(state => { if (state.isConnected) { syncPendingTransactions(); } }); }; const syncPendingTransactions = async () => { // Get all unsynced transactions const pendingTransactions = await getPendingTransactions(); for (const transaction of pendingTransactions) { try { // Send update to server await axios.put(`https://your-api.com/transactions/${transaction.id}`, { status: transaction.status }); // Mark as synced in local DB await markTransactionAsSynced(transaction.id); } catch (error) { console.error('Failed to sync transaction:', transaction.id, error); // Leave it as unsynced to try again next time network is back } } }; const getPendingTransactions = () => { return new Promise((resolve, reject) => { db.transaction(tx => { tx.executeSql( 'SELECT * FROM transactions WHERE isSynced = 0', [], (_, { rows }) => resolve(rows._array), (_, error) => reject(error) ); }); }); }; const markTransactionAsSynced = (transactionId) => { return new Promise((resolve, reject) => { db.transaction(tx => { tx.executeSql( 'UPDATE transactions SET isSynced = 1 WHERE id = ?', [transactionId], (_, result) => resolve(result), (_, error) => reject(error) ); }); }); };
Initialize the sync listener when your app starts (e.g., in App.js):
// App.js import { useEffect } from 'react'; import { initDatabase } from './db'; import { setupSyncListener } from './SyncService'; import TransactionList from './TransactionList'; export default function App() { useEffect(() => { const initApp = async () => { await initDatabase(); setupSyncListener(); }; initApp(); }, []); return <TransactionList />; }
- Error Handling: Add more robust error handling for API calls and database operations (e.g., exponential backoff for failed syncs).
- Conflict Resolution: If the server updates a transaction while the user is offline, add a
versionfield to track which data is newer (server version takes priority, or prompt the user to resolve conflicts). - Loading States: Add loading spinners when fetching data or syncing changes to keep users informed.
内容的提问来源于stack exchange,提问作者DCoder

