Skip to main content

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:

  1. ๐Ÿ”„ Loading toast appears with spinner animation
  2. โœ… Seamlessly transitions to success toast on resolve
  3. โŒ Or transitions to error toast on rejection
  4. ๐Ÿงน 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:


โšก Promise integration transforms async operations from boring loading spinners into delightful, informative experiences that keep users engaged!