Setup and context — Why Error Handling Matters
When you first start building apps, it's natural to focus on adding exciting new features. But what truly matters to users is that your app works reliably. No matter how innovative your features are, an app that crashes frequently won't retain users for long.
With Rork's AI-powered code generation, basic error handling can sometimes be overlooked as the AI focuses on getting the happy path working. This guide covers the essential error handling and crash prevention techniques that will make your Rork app robust and reliable, even if you're just getting started with app development.
By the end of this article, you'll understand how to:
- Apply fundamental error handling patterns in JavaScript and React Native
- Handle API communication failures gracefully
- Design your app to prevent crashes before they happen
- Debug issues efficiently when they do occur
try-catch — The Foundation of Error Handling
The most fundamental error handling technique in JavaScript is try-catch. You wrap potentially failing code in a try block, and if an error occurs, the catch block handles it gracefully.
// Basic try-catch pattern in a Rork app
const fetchUserData = async (userId) => {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
// Check HTTP status code
if (!response.ok) {
throw new Error(`Server error: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
// Handle different types of errors
if (error.message.includes('Network request failed')) {
console.warn('Please check your network connection');
} else {
console.error('Failed to fetch data:', error.message);
}
return null; // Return a fallback value
}
};
// Expected output (success): User data object
// Expected output (error): null (+ error message in console)The key principle here is to never silently swallow errors. Always provide meaningful feedback to users — tell them something went wrong and suggest what they can do next (retry, check their connection, etc.).
Error Boundaries — A Safety Net for Your UI
In React Native apps, an unhandled error during rendering will crash the entire application. Error Boundaries prevent this by catching rendering errors in child components and displaying a fallback UI instead of a blank screen.
// ErrorBoundary component implementation
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
// Update state to show fallback UI on next render
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// Send error logs to a monitoring service (important for production)
console.error('Error Boundary caught:', error, errorInfo);
}
handleRetry = () => {
this.setState({ hasError: false, error: null });
};
render() {
if (this.state.hasError) {
return (
<View style={styles.container}>
<Text style={styles.title}>Something went wrong</Text>
<Text style={styles.message}>
We're sorry. An error occurred while loading this screen.
</Text>
<TouchableOpacity style={styles.button} onPress={this.handleRetry}>
<Text style={styles.buttonText}>Try Again</Text>
</TouchableOpacity>
</View>
);
}
return this.props.children;
}
}
// Expected behavior:
// Child component error → Fallback UI displayed
// "Try Again" tap → Child component re-rendersPlace Error Boundaries strategically. Rather than wrapping your entire app in a single boundary, add them at each screen level and around sections that depend on external data. This way, an error in one part of your app won't bring down the whole experience.
Handling Network Errors — Offline Support and Retries
On mobile devices, unreliable network connections are a fact of life. Weak signal areas, Wi-Fi transitions, airplane mode — there are countless scenarios where network requests will fail.
When building network communication in your Rork app, keep these three strategies in mind.
Check Connection Status
import NetInfo from '@react-native-community/netinfo';
// Custom hook for monitoring network status
const useNetworkStatus = () => {
const [isConnected, setIsConnected] = React.useState(true);
React.useEffect(() => {
const unsubscribe = NetInfo.addEventListener((state) => {
setIsConnected(state.isConnected);
});
return () => unsubscribe();
}, []);
return isConnected;
};
// Usage example
const MyScreen = () => {
const isConnected = useNetworkStatus();
if (!isConnected) {
return <OfflineBanner message="No internet connection" />;
}
return <MainContent />;
};Set Request Timeouts
Always set timeouts on API requests so your app doesn't hang indefinitely waiting for a response.
// Fetch with timeout implementation
const fetchWithTimeout = async (url, options = {}, timeout = 10000) => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
...options,
signal: controller.signal,
});
clearTimeout(timeoutId);
return response;
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error('Request timed out');
}
throw error;
}
};
// Expected output (on timeout): Error: Request timed outImplement Retry Logic
For transient errors, automatic retries with exponential backoff can dramatically improve the user experience.
// Retry with exponential backoff
const fetchWithRetry = async (url, options = {}, maxRetries = 3) => {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetchWithTimeout(url, options);
if (response.ok) return response;
// Retry on 5xx server errors
if (response.status >= 500) {
throw new Error(`Server error: ${response.status}`);
}
// Don't retry on 4xx client errors
return response;
} catch (error) {
if (attempt === maxRetries - 1) throw error;
// Exponential backoff: 1s, 2s, 4s...
const delay = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
};For more practical patterns on handling network-related crashes, check out the Complete Guide to Fixing Launch Crashes and White Screens in Rork Apps.
Input Validation — Preventing Errors from User Input
Always treat user input as potentially unexpected. Form entries, search queries, settings — validate everything to prevent crashes before they reach your business logic.
// Input validation utility functions
const validateInput = {
// Email format check
isValidEmail: (email) => {
if (!email || typeof email !== 'string') return false;
const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return pattern.test(email.trim());
},
// Numeric range check
isInRange: (value, min, max) => {
const num = Number(value);
return !isNaN(num) && num >= min && num <= max;
},
// String length check
isValidLength: (text, minLen, maxLen) => {
if (!text || typeof text !== 'string') return false;
const trimmed = text.trim();
return trimmed.length >= minLen && trimmed.length <= maxLen;
},
};
// Usage in a form submission handler
const handleSubmit = (formData) => {
const errors = [];
if (!validateInput.isValidEmail(formData.email)) {
errors.push('Please enter a valid email address');
}
if (!validateInput.isValidLength(formData.name, 1, 50)) {
errors.push('Name must be between 1 and 50 characters');
}
if (errors.length > 0) {
// Display error messages
Alert.alert('Input Error', errors.join('\n'));
return;
}
// Proceed after validation passes
submitData(formData);
};Remember that validation should happen on both the client side and the server side. Client-side validation improves user experience by providing immediate feedback, while server-side validation ensures security and data integrity.
Debugging Techniques — Finding Problems Fast
When errors occur, the ability to quickly identify the root cause is an invaluable skill. Here are essential debugging techniques for your Rork app development workflow.
Using Console Methods Effectively
// console.log — General information output
console.log('User data:', userData);
// console.warn — Warnings (yellow in console)
console.warn('API response contains unexpected fields');
// console.error — Errors (red in console)
console.error('Failed to retrieve auth token:', error);
// console.table — Display objects/arrays in table format
console.table([
{ name: 'API call', duration: '120ms', status: 'success' },
{ name: 'DB write', duration: '45ms', status: 'success' },
]);
// console.time / timeEnd — Measure execution time
console.time('Data fetch');
const data = await fetchUserData();
console.timeEnd('Data fetch');
// Expected output: Data fetch: 235.42msConditional Debug Logging
Make sure debug logs don't leak into production by using environment-based controls.
// Debug utility
const DEBUG = __DEV__; // React Native development mode flag
const logger = {
debug: (...args) => {
if (DEBUG) console.log('[DEBUG]', ...args);
},
warn: (...args) => {
console.warn('[WARN]', ...args);
},
error: (...args) => {
console.error('[ERROR]', ...args);
// In production, send to error reporting service
if (!DEBUG) {
// reportError(args);
}
},
};
// Usage
logger.debug('Current state:', state); // Only in development
logger.error('Payment error:', error); // Always logs + reports in productionTo take your error monitoring to the next level with tools like Sentry and Firebase Crashlytics, see The Complete Guide to Crash Analytics and Monitoring for Rork Max Apps.
Summary
Error handling and crash prevention form the foundation of app quality. By applying the techniques covered in this article, you can significantly improve your Rork app's stability and user experience.
Start with basic try-catch error catching, add Error Boundaries at strategic points, implement network error handling with retries, and build up your debugging skills gradually. You don't need to achieve perfection — the first goal is simply making your app resilient enough that errors don't crash the entire experience.
For advanced error handling patterns and production monitoring strategies, Error Handling, Debugging, and Production Monitoring Patterns for Rork Max provides a comprehensive deep-dive.