Promise Integration โก
One of RN-Tosty's most powerful features is seamless promise integration. Handle async operations with beautiful loading states, automatic success/error handling, and smooth transitions.
๐ The Magic of Promise Toastsโ
Traditional approach (verbose and error-prone):
// โ Old way - manual state management
const [loading, setLoading] = useState(false);
const handleSave = async () => {
setLoading(true);
toast.info('Saving...'); // Manual loading toast
try {
const result = await api.saveProfile(data);
toast.dismiss(); // Remember to dismiss loading
toast.success('Profile saved!');
return result;
} catch (error) {
toast.dismiss(); // Don't forget this!
toast.error('Save failed!');
throw error;
} finally {
setLoading(false);
}
};
RN-Tosty approach (elegant and automatic):
// โ
New way - automatic state management
const handleSave = () => {
return toast.promise(api.saveProfile(data), {
loading: 'Saving your profile...',
success: 'Profile saved successfully!',
error: 'Failed to save profile',
});
};
What happens automatically:
- ๐ Loading toast appears with spinner animation
- โ Seamlessly transitions to success toast on resolve
- โ Or transitions to error toast on rejection
- ๐งน Automatically cleans up and manages state
๐ Basic Usageโ
Simple Promise Handlingโ
import { toast } from 'rn-tosty';
// Basic promise integration
const uploadFile = async (file) => {
return toast.promise(
api.uploadFile(file), // Your promise
{
loading: 'Uploading file...',
success: 'File uploaded successfully!',
error: 'Upload failed',
}
);
};
Dynamic Messagesโ
Messages can be functions that receive the resolved/rejected value:
const saveUser = async (userData) => {
return toast.promise(api.createUser(userData), {
loading: 'Creating user account...',
success: (user) => `Welcome, ${user.name}! Your account is ready.`,
error: (error) => `Failed to create account: ${error.message}`,
});
};
๐จ Advanced Configurationโ
Custom Loading Iconsโ
Choose from different loading animations:
toast.promise(fetchData(), {
loading: {
message: 'Loading your data...',
icon: {
type: 'spinner', // 'spinner', 'dots', 'bars', 'pulse'
size: 'large', // 'small', 'medium', 'large', or number
color: '#3B82F6', // Custom color
},
},
success: 'Data loaded!',
error: 'Loading failed',
});
Multiple Step Operationsโ
Handle complex multi-step operations:
const syncAllData = async () => {
const steps = [
{ fn: () => api.syncUsers(), message: 'Syncing users...' },
{ fn: () => api.syncPosts(), message: 'Syncing posts...' },
{ fn: () => api.syncComments(), message: 'Syncing comments...' },
];
const results = [];
for (let i = 0; i < steps.length; i++) {
const step = steps[i];
const result = await toast.promise(step.fn(), {
loading: `${step.message} (${i + 1}/${steps.length})`,
success: (data) =>
`${step.message.replace('ing...', 'ed!')} ${data.length} items`,
error: (err) => `${step.message.replace('Syncing', 'Failed to sync')}`,
});
results.push(result);
}
return results;
};
๐ฏ Real-World Examplesโ
E-commerce Checkoutโ
const processCheckout = async (orderData) => {
// Step 1: Validate payment
const payment = await toast.promise(api.validatePayment(orderData.payment), {
loading: {
message: 'Validating payment method...',
icon: { type: 'spinner', size: 'medium' },
},
success: 'Payment method validated โ',
error: (err) => `Payment validation failed: ${err.message}`,
});
// Step 2: Process order
const order = await toast.promise(
api.processOrder({ ...orderData, paymentId: payment.id }),
{
loading: {
message: 'Processing your order...',
icon: { type: 'bars', size: 'large' },
},
success: (order) => `Order #${order.id} confirmed! ๐`,
error: 'Order processing failed. Your card was not charged.',
},
{
duration: 6000, // Show success longer
priority: 'high',
}
);
return order;
};
File Upload with Progressโ
const uploadImages = async (images) => {
const uploads = images.map(async (image, index) => {
return toast.promise(
api.uploadImage(image),
{
loading: {
message: `Uploading ${image.name}... (${index + 1}/${images.length})`,
icon: { type: 'pulse', color: '#3B82F6' },
},
success: (result) => `๐ธ ${image.name} uploaded (${result.size} KB)`,
error: (err) => `โ Failed to upload ${image.name}: ${err.message}`,
},
{
position: 'bottom',
duration: 3000,
}
);
});
return Promise.all(uploads);
};
Social Media Postโ
const publishPost = async (postData) => {
return toast.promise(
api.createPost(postData),
{
loading: {
message: 'Publishing your post...',
icon: { type: 'dots', size: 'medium' },
},
success: (post) => {
const likes = post.likes || 0;
const shares = post.shares || 0;
return `๐ Post published! ${likes} likes, ${shares} shares`;
},
error: (error) => {
if (error.code === 'CONTENT_MODERATION') {
return '๐ซ Post blocked by content moderation';
}
return `๐ Failed to publish: ${error.message}`;
},
},
{
variant: 'social-success', // Custom variant for social actions
duration: 5000,
}
);
};
Data Sync with Retryโ
const syncWithRetry = async (data, maxRetries = 3) => {
let attempt = 0;
const attemptSync = async () => {
attempt++;
try {
return await api.syncData(data);
} catch (error) {
if (attempt < maxRetries && error.code === 'NETWORK_ERROR') {
// Wait before retry
await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
return attemptSync();
}
throw error;
}
};
return toast.promise(attemptSync(), {
loading: {
message: 'Syncing data...',
icon: { type: 'spinner' },
},
success: (result) => `โ
Synced ${result.count} items successfully`,
error: (error) => {
if (attempt >= maxRetries) {
return `โ Sync failed after ${maxRetries} attempts: ${error.message}`;
}
return `โ Sync failed: ${error.message}`;
},
});
};
๐๏ธ Configuration Optionsโ
Promise Messages and Promise Configurationโ
interface PromiseMessages<T = any> {
loading: PromiseMessage<T>;
success: PromiseMessage<T>;
error: PromiseErrorMessage;
}
type PromiseMessage<T = any> =
| string
| ((data: T) => string)
| PromiseToastConfig
| ((data: T) => PromiseToastConfig);
interface PromiseConfig {
position?: ToastPosition;
/** Global layout configuration applied to all promise toast states */
layout?: ToastLayoutConfig;
}
Promise Configuration Examplesโ
toast.promise(mockApiSuccess(2000), {
loading: {
icon: { type: 'bars' },
message: 'Loading success...',
},
success: (data) => `Welcome ${data.name}! (${data.status})`,
error: (err) => `Failed: ${err.message}`,
});
toast.promise<{ id: number; name: string }>(
mockApiError(),
{
loading: {
message: 'Loading error...',
icon: { type: 'spinner', size: 'large' },
},
success: (data) => `Data loaded successfully: ${data?.name ?? ''}`,
error: (err) => `Error loading data: ${err.message}`,
},
{
layout: {
iconPosition: 'right',
textAlignment: 'left',
spacing: 'spacious',
},
}
);
toast.promise<{ id: number; name: string }>(mockApiError(), {
loading: {
message: 'Loading error...',
icon: { type: 'spinner', size: 'large' },
variant: 'showcase-error',
},
success: (data) => ({
message: `Data loaded successfully: ${data?.name ?? ''}`,
variant: 'showcase-success',
title: 'Success',
}),
error: (err) => ({
message: `Error loading data: ${err.message}`,
variant: 'showcase-error',
title: 'Error',
}),
});
๐จ Loading Icon Typesโ
Spinner (Default)โ
icon: {
type: 'spinner';
}
Classic spinning circle - works great for most operations.
Dotsโ
icon: {
type: 'dots';
}
Three bouncing dots - perfect for messaging or chat operations.
Barsโ
icon: {
type: 'bars';
}
Animated equalizer bars - great for audio/video processing or data analysis.
Pulseโ
icon: {
type: 'pulse';
}
Pulsing circle - ideal for sync operations or heartbeat-like processes.
Custom Iconsโ
import { CustomIconComponent } from 'rn-tosty';
const CustomLoadingIcon: CustomIconComponent = ({ size, color }) => (
<YourAnimatedIcon size={size} color={color} />
);
toast.promise(operation(), {
loading: {
message: 'Processing...',
icon: CustomLoadingIcon,
},
// ...
});
๐ง Error Handling Strategiesโ
Graceful Error Messagesโ
const handleApiError = (error) => {
// Network errors
if (error.code === 'NETWORK_ERROR') {
return 'Connection lost. Please check your internet.';
}
// Server errors
if (error.status >= 500) {
return 'Server error. Our team has been notified.';
}
// Client errors
if (error.status === 401) {
return 'Session expired. Please log in again.';
}
if (error.status === 403) {
return "You don't have permission for this action.";
}
// Default fallback
return error.message || 'Something went wrong. Please try again.';
};
// Use in promise
toast.promise(riskyOperation(), {
loading: 'Processing...',
success: 'Done!',
error: handleApiError,
});
Type-Safe Error Handlingโ
interface ApiError {
code: string;
message: string;
details?: any;
}
const typedApiCall = (): Promise<{ id: string; name: string }> => {
return api.getData();
};
toast.promise(typedApiCall(), {
loading: 'Loading data...',
success: (data) => `Loaded ${data.name} successfully!`,
error: (error: ApiError) => {
switch (error.code) {
case 'NOT_FOUND':
return 'Data not found. It may have been deleted.';
case 'PERMISSION_DENIED':
return 'Access denied. Please contact your administrator.';
default:
return `Error: ${error.message}`;
}
},
});
๐ Performance Tipsโ
1. Avoid Promise Spamโ
// โ Don't create promise toast for every action
onClick={() => {
// These will create multiple loading toasts
toast.promise(api.like(postId), { /* ... */ });
toast.promise(api.follow(userId), { /* ... */ });
toast.promise(api.bookmark(postId), { /* ... */ });
}}
// โ
Batch operations or use different approaches
onClick={() => {
// Option 1: Batch the operations
toast.promise(
Promise.all([
api.like(postId),
api.follow(userId),
api.bookmark(postId)
]),
{
loading: 'Processing actions...',
success: 'All actions completed!',
error: 'Some actions failed'
}
);
// Option 2: Use regular toasts for quick actions
api.like(postId).then(() => toast.success('Post liked!'));
}}
2. Memory Managementโ
// For long-running operations, store the promise reference
const longOperation = useRef<Promise<any> | null>(null);
const handleLongOperation = () => {
// Cancel previous operation if still running
if (longOperation.current) {
toast.dismiss(); // Clear any existing promise toasts
}
longOperation.current = toast.promise(heavyComputation(), {
/* configuration */
});
};
๐ฏ Best Practicesโ
1. Clear Loading Messagesโ
// โ Vague
loading: 'Loading...';
// โ
Specific
loading: 'Uploading your profile photo...';
loading: 'Saving draft to cloud...';
loading: 'Processing payment...';
2. Helpful Success Messagesโ
// โ Generic
success: 'Success!';
// โ
Informative
success: (user) => `Welcome, ${user.name}! Your account is ready.`;
success: (result) => `${result.count} items synced successfully`;
3. User-Friendly Error Messagesโ
// โ Technical
error: (err) => `HTTP 500: ${err.stack}`;
// โ
User-friendly
error: (err) => {
if (err.code === 'NETWORK_ERROR') {
return 'Check your internet connection and try again.';
}
return 'Something went wrong. Please try again.';
};
4. Appropriate Iconsโ
// File operations
icon: {
type: 'bars';
}
// Network requests
icon: {
type: 'spinner';
}
// Real-time sync
icon: {
type: 'pulse';
}
// Messaging
icon: {
type: 'dots';
}
๐ Next Stepsโ
Master promise integration with these advanced topics:
- Queue Management โ - Handle multiple promises
- Custom Icons โ - Create custom loading animations
- Error Handling โ - Robust error management
- Performance โ - Optimization strategies
โก Promise integration transforms async operations from boring loading spinners into delightful, informative experiences that keep users engaged!