React Native uses the Fetch API for network requests, providing a Promise-based interface that works consistently across iOS and Android platforms. The API includes built-in support for standard HTTP methods, JSON parsing, and request configuration options.
// Basic GET requestconst response = await fetch('https://api.example.com/data');if (!response.ok) throw new Error('Request failed');const data: any = await response.json();// POST request with configurationconst postResponse = await fetch('https://api.example.com/users', {method: 'POST',headers: { 'Content-Type': 'application/json' },});const user: any = await postResponse.json();
API responses in React Native frequently contain extra metadata, inconsistent field names, or structures not directly compatible with components like <FlatList>. A transformation step reshapes this data, optimizing it for fast rendering and features such as search, by producing a predictable format expected by the component.
const transformForFlatList = (data: { Results: { Make_ID: number; Make_Name: string }[] }) =>data.Results.map(item => ({id: item.Make_ID,title: item.Make_Name.toLowerCase().split(' ').map(word => word[0].toUpperCase() + word.slice(1)).join(' '),searchableText: item.Make_Name.toLowerCase(),}));// Example usage:const apiResponse = {Count: 10426,Message: "Response returned successfully",Results: [{ Make_ID: 440, Make_Name: "ASTON MARTIN" },{ Make_ID: 441, Make_Name: "TESLA" }]};const flatListData = transformForFlatList(apiResponse);
AsyncStorage UsageAsyncStorage offers an asynchronous key-value storage solution for React Native, suitable for user preferences, tokens, or small data on both iOS and Android. All values are stored as strings, so objects must be serialized before storage and parsed after retrieval.
import AsyncStorage from '@react-native-async-storage/async-storage';// Saving a string valueawait AsyncStorage.setItem('@username', 'john_doe');// Saving an objectconst user = { id: 123, name: 'John Doe', theme: 'dark' };await AsyncStorage.setItem('@user_data', JSON.stringify(user));// Retrieving valuesconst username = await AsyncStorage.getItem('@username');const userDataString = await AsyncStorage.getItem('@user_data');const userData = userDataString? JSON.parse(userDataString) as { id: number; name: string; theme: string }: null;
React Native includes several data persistence options. For basic key-value storage, AsyncStorage provides asynchronous, unencrypted local storage for small, non-sensitive data. For structured data requirements, solutions such as expo-sqlite support relational databases. Encrypted storage of sensitive information can be achieved with modules like expo-secure-store.
Selection of a storage solution depends on data size, sensitivity, retrieval speed, and security requirements.
Caching in React Native refers to storing data or resources locally on the device to speed up access and reduce unnecessary network requests. This improves app performance, makes frequently used data available offline, and results in a smoother user experience.
AsyncStorage provides a way to cache frequently accessed data by saving it on the device. Caching strategies should be chosen based on the type of data, the need for persistence, and the desired balance between speed, storage, and freshness of content.
import AsyncStorage from '@react-native-async-storage/async-storage';// Save data with expiration (default: 30 minutes)const createCache = async (key: string, data: any, minutes = 30) => {const cacheEntry = {data,expires: Date.now() + minutes * 60 * 1000,};await AsyncStorage.setItem(`cache:${key}`, JSON.stringify(cacheEntry));};// Retrieve cached data if not expired; otherwise, remove the cache and return nullconst getCache = async (key: string): Promise<any | null> => {const cached = await AsyncStorage.getItem(`cache:${key}`);if (!cached) return null;const entry = JSON.parse(cached);if (Date.now() > entry.expires) {await AsyncStorage.removeItem(`cache:${key}`);return null;}return entry.data;};
The offline-first pattern describes an approach where data is loaded from local storage first, providing instant results before attempting to fetch updates from the network. In React Native, AsyncStorage is often used to cache data locally, ensuring apps remain responsive and functional even without network connectivity.
import AsyncStorage from '@react-native-async-storage/async-storage';const loadDataOfflineFirst = async (setData: (data: any) => void) => {// Load cached data immediatelyconst localData = await AsyncStorage.getItem('cached_data');if (localData) setData(JSON.parse(localData));// Fetch fresh data and update cache in backgroundtry {const response = await fetch('/api/data');const freshData = await response.json();await AsyncStorage.setItem('cached_data', JSON.stringify(freshData));setData(freshData);} catch {// Continue using cached data if fetch failsconsole.log('Using cached data due to network error');}};
Providing user feedback during network operations is essential in mobile apps, as connectivity can be slow or unreliable, and operations may take longer than expected. Clear indicators such as loading spinners, error messages, or status bars help users understand what is happening and prevent confusion or frustration.
In React Native, common patterns for user feedback involve managing loading and error states, displaying activity indicators, and visually signaling whether data is loaded from the network or cache.
import { useState } from 'react';import { ActivityIndicator, Text, View } from 'react-native';// State management for feedbackconst [loading, setLoading] = useState(false);const [error, setError] = useState<string | null>(null);// In render{loading && <ActivityIndicator size="large" color="#007AFF" />}{error && <Text>Failed to load data. Please try again.</Text>}// Status indicator color for network vs. cacheconst statusColor = dataSource === 'network' ? '#34C759' : '#FF9500';<View style={[styles.indicator, { backgroundColor: statusColor }]} />
Pull-to-refresh is a standard interaction in mobile apps, enabling users to manually update data by pulling down on a scrollable list. In React Native, the <RefreshControl> component provides this behavior for lists like <FlatList> and <ScrollView>, displaying a loading indicator and triggering a refresh function when activated.
<RefreshControl> gives users direct control over data freshness and improves the app experience when automatic updates are insufficient or network conditions are variable.
import { FlatList, RefreshControl } from 'react-native';// State for refresh controlconst [refreshing, setRefreshing] = useState(false);const [data, setData] = useState<any[]>([]);// Refresh handlerconst handleRefresh = async () => {setRefreshing(true);try {const freshData = await fetchDataFromAPI();setData(freshData);} finally {setRefreshing(false);}};// In the component render<FlatListdata={data}renderItem={renderItem}refreshControl={<RefreshControlrefreshing={refreshing}onRefresh={handleRefresh}colors={['#007AFF']}tintColor="#007AFF"/>}/>
Mobile apps must address data staleness caused by app lifecycle events, such as transitioning between background and foreground. Unlike web applications, which reload on each visit, mobile apps often pause data fetching when moved to the background and resume with potentially outdated information. As a result, effective strategies are needed to refresh or validate data when the app returns to the foreground, ensuring users always see up-to-date content.