Handling Networking and APIs
1. Using Fetch API
| Property | Description | Example |
|---|---|---|
| fetch() | Method to make HTTP requests | fetch('https://api.example.com/data') |
| method | HTTP method (GET, POST, PUT, DELETE) | { method: 'POST' } |
| headers | Request headers | { 'Content-Type': 'application/json' } |
| body | Request body (for POST/PUT) | JSON.stringify({ name: 'John' }) |
Example: Fetch GET request
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
Example: Fetch POST request
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: 'My Post',
body: 'This is the content'
})
})
.then(response => response.json())
.then(data => console.log(data));
Note: React Native's fetch API is based on the web standard, but does not support all features like cookies or credentials by default. For more advanced networking needs, consider using libraries like
axios.
2. Handling Request Headers
| Header | Description | Usage |
|---|---|---|
| Authorization | Used to provide credentials for authentication | headers: { 'Authorization': 'Bearer token123' } |
| Content-Type | Indicates the media type of the resource | headers: { 'Content-Type': 'application/json' } |
| Accept | Specifies the media types that are acceptable for the response | headers: { 'Accept': 'application/json' } |
| User-Agent | Identifies the client making the request | headers: { 'User-Agent': 'ReactNativeApp/1.0' } |
| Cache-Control | Directs caching mechanisms for the request/response | headers: { 'Cache-Control': 'no-cache' } |
Example: Setting Custom Headers in Fetch
fetch('https://api.example.com/data', {
method: 'GET',
headers: {
'Authorization': 'Bearer token123',
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': 'ReactNativeApp/1.0'
}
})
.then(response => response.json())
.then(data => console.log(data));
Example: Sending Headers with POST Request
const postData = {
title: 'My Post',
content: 'This is the content'
};
fetch('https://api.example.com/posts', {
method: 'POST',
headers: {
'Authorization': 'Bearer token123',
'Content-Type': 'application/json'
},
body: JSON.stringify(postData)
})
.then(response => response.json())
.then(data => console.log('Post created:', data));
3. Managing API Responses
| Property | Description | Example |
|---|---|---|
| status | HTTP status code of the response | 200, 404 |
| statusText | Readable status message | "OK", "Not Found" |
| headers | Response headers object | {"content-type": "application/json"} |
| ok | Boolean indicating if status is in 2xx range | true, false |
Example: Handling API Response
const response = await fetch('https://api.example.com/data');
if (response.ok) {
const data = await response.json();
console.log(data);
} else {
console.error('Failed to fetch:', response.status, response.statusText);
}
Note: Always check
response.ok before parsing JSON to avoid runtime errors from invalid responses.
Key Points
- status and statusText provide HTTP status information.
- headers can be used to read response metadata like content type.
- ok is a convenient boolean to check for success status codes (2xx).
- Always handle error cases when fetching data from external APIs.
4. Implementing Error Handling
Example: Basic Network Error Handling
import React, { useState } from 'react';
const ApiComponent = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
return (
<div>
{loading && <p>Loading...</p>}
{error && <p>Error: {error}</p>}
{data && <p>Data: {JSON.stringify(data)}</p>}
</div>
);
};
export default ApiComponent;
| Error Type | Description | Handling Strategy |
|---|---|---|
| Network Error | Connection issues, timeouts, or offline state | Retry logic with exponential backoff, user notification |
| HTTP Status Error | Server returns 4xx or 5xx status codes | Parse error code, show user-friendly message |
| Parse Error | Invalid JSON or data format | Validate response structure, fallback to defaults |
Note: Always handle network errors gracefully to prevent app crashes and provide a better user experience.
Example: Retry Logic with Exponential Backoff
const fetchWithRetry = async (url, retries = 3, delay = 1000) => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
if (retries > 0) {
console.log(`Retrying... ${retries} attempts left`);
await new Promise(resolve => setTimeout(resolve, delay));
return fetchWithRetry(url, retries - 1, delay * 2);
} else {
throw new Error("Max retries exceeded");
}
}
};
// Usage
fetchWithRetry('https://api.example.com/data')
.then(data => console.log(data))
.catch(err => console.error("Failed after retries:", err));
Error Handling Best Practices
- Implement centralized error logging using tools like Sentry or Bugsnag
- Provide user-friendly messages instead of raw error codes
- Use retry mechanisms for transient network issues
- Gracefully degrade UI when API calls fail
5. Using Abort Controller
| Feature | Description | Usage |
|---|---|---|
| AbortController | API to signal cancellation of asynchronous operations | const controller = new AbortController(); const signal = controller.signal; |
| abortSignal | Signal used to cancel fetch requests or other async tasks | fetch('/api/data', { signal }); |
| abort() | Method to trigger cancellation of the operation | controller.abort(); |
Example: Canceling a Fetch Request
const controller = new AbortController();
const signal = controller.signal;
fetch('/api/data', { signal })
.then(response => response.json())
.then(data => console.log(data))
.catch(err => {
if (err.name === 'AbortError') {
console.log('Fetch was aborted');
} else {
console.error('Fetch error:', err);
}
});
// Cancel the request after 2 seconds
setTimeout(() => {
controller.abort();
}, 2000);
Note: The
AbortController is part of the Web API and supported in React Native environments.
6. Handling Network Errors
| Error Type | Description | Handling Strategy | Example |
|---|---|---|---|
| Network Error | Occurs when the device cannot connect to the network or server. | Use try-catch blocks and display user-friendly messages. | |
| Timeout Error | Request exceeds the set timeout limit. | Implement retry logic or show timeout-specific UI. | |
| HTTP Error (4xx/5xx) | Server returns an error status code (e.g., 404, 500). | Parse status codes and provide actionable feedback. | |
Example: Error Boundary in React Native
import React from 'react';
import { View, Text } from 'react-native';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Caught an error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<View>
<Text>Something went wrong.</Text>
</View>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
Note: Implementing error boundaries helps in gracefully handling runtime errors in React Native components.
7. Implementing Request Timeouts
| Property | Description | Default Value | Notes |
|---|---|---|---|
| timeout | Timeout in milliseconds for the request | 5000 |
Used in fetch and XMLHttpRequest |
Example: Setting Timeout with Fetch
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
fetch('https://api.example.com/data', {
signal: controller.signal
})
.then(response => response.json())
.then(data => console.log(data))
.catch(err => {
if (err.name === 'AbortError') {
console.error('Request timed out');
} else {
console.error('Fetch error:', err);
}
});
Note: The
timeout property is not a native option in the fetch API, but can be implemented using AbortController.
Warning: When using
AbortController, ensure that the timeout is cleared if the request completes before the timeout to prevent unnecessary aborts.
8. Using FormData
| Property | Description | Usage |
|---|---|---|
| new FormData() | Creates a new FormData object | const formData = new FormData(); |
| formData.append() | Adds a new field to the FormData object | formData.append('key', 'value'); |
| formData.append() | Adds a file to the FormData object | formData.append('file', fileBlob, 'filename.jpg'); |
| formData.get() | Retrieves the first value of a field | const value = formData.get('key'); |
| formData.getAll() | Retrieves all values of a field | const values = formData.getAll('key'); |
| formData.delete() | Deletes a field from FormData | formData.delete('key'); |
| formData.has() | Checks if a field exists | const exists = formData.has('key'); |
| formData.entries() | Returns an iterator of all key-value pairs | for (const [key, value] of formData.entries()) { ... } |
Example: Sending FormData with fetch
const formData = new FormData();
formData.append('name', 'John Doe');
formData.append('avatar', fileBlob, 'avatar.jpg');
fetch('https://api.example.com/upload', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => console.log(data));
Note: FormData is not supported natively in React Native's fetch implementation. You may need to use a library like
react-native-fetch-blob or axios for better FormData support.
9. Implementing Retry Logic
Example: Basic Retry with Exponential Backoff
const retryFetch = async (url, options = {}, maxRetries = 3) => {
let lastError;
for (let i = 0; i <= maxRetries; i++) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
} catch (error) {
lastError = error;
if (i === maxRetries) break;
const delay = Math.pow(2, i) * 1000; // Exponential backoff
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
};
// Usage
retryFetch('https://api.example.com/data')
.then(data => console.log(data))
.catch(error => console.error('Failed after retries:', error));
| Option | Values | Description |
|---|---|---|
| maxRetries | 3 |
Maximum number of retry attempts |
| backoffStrategy | 'exponential' |
Delay strategy between retries |
| delayBase | 1000 |
Base delay in milliseconds (multiplied by exponential factor) |
Note: This example uses exponential backoff to avoid overwhelming the server with rapid retries.
Warning: Be cautious with retry logic in mobile apps as it can lead to increased data usage and battery drain.
10. Handling Offline Scenarios
Example: Detecting Network Status
import { NetInfo } from 'react-native';
NetInfo.fetch().then(state => {
console.log("Connection type", state.type);
console.log("Is connected", state.isConnected);
});
| Property | Description | Values |
|---|---|---|
| isConnected | Indicates if the device is connected to a network | true / false |
| type | Network type (e.g., WiFi, cellular) | "none", "wifi", "cellular" |
| isInternetReachable | Checks if the device can reach the internet | true / false |
Note: For newer versions of React Native, use
NetInfo from @react-native-community/netinfo package.
Example: Using NetInfo with useEffect
import React, { useEffect, useState } from 'react';
import NetInfo from '@react-native-community/netinfo';
const NetworkStatus = () => {
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
const unsubscribe = NetInfo.addEventListener(state => {
setIsConnected(state.isConnected);
});
return () => unsubscribe();
}, []);
return (
<View>
<Text>{isConnected ? "Online" : "Offline"}</Text>
</View>
);
};
Best Practices for Offline Handling
- Always check network status before making API calls
- Implement retry logic with exponential backoff for failed requests
- Cache data locally using libraries like AsyncStorage or SQLite
- Provide user feedback when the app is offline
11. Using WebSocket Connections
| Property | Description | Example |
|---|---|---|
| WebSocket | Native WebSocket API for real-time communication | new WebSocket('ws://localhost:8080') |
| url | WebSocket server URL | 'wss://echo.websocket.org' |
| readyState | Current state of the connection (0-3) | WebSocket.CONNECTING |
Example: Sending and Receiving Messages
const ws = new WebSocket('ws://localhost:8080');
ws.onopen = () => {
console.log('Connected');
ws.send('Hello Server');
};
ws.onmessage = (event) => {
console.log('Received:', event.data);
};
ws.onclose = () => {
console.log('Disconnected');
};
Note: React Native's WebSocket implementation is based on the Web API but may differ in behavior from browser implementations.
12. Using Axios Library
Example: Basic GET Request
import axios from 'axios';
const fetchData = async () => {
try {
const response = await axios.get('https://api.example.com/data');
console.log(response.data);
} catch (error) {
console.error('Error fetching data:', error);
}
};
fetchData();
| Property | Description | Default |
|---|---|---|
| url | The URL to request data from | None |
| method | The HTTP method to use (GET, POST, PUT, DELETE) | GET |
| headers | HTTP headers to send with the request | None |
| data | Data to send in the request body (for POST/PUT) | None |
Note: Axios automatically handles JSON parsing and can be used in both React Native and web environments.
Example: POST Request with Data
import axios from 'axios';
const postData = async () => {
try {
const response = await axios.post('https://api.example.com/users', {
name: 'John Doe',
email: 'john@example.com'
});
console.log('User created:', response.data);
} catch (error) {
console.error('Error creating user:', error);
}
};
postData();