Always wrap asynchronous operations in try-catch-finally blocks to prevent unhandled promise rejections and ensure proper cleanup. When errors occur, provide users with meaningful feedback while preserving application state.
Always wrap asynchronous operations in try-catch-finally blocks to prevent unhandled promise rejections and ensure proper cleanup. When errors occur, provide users with meaningful feedback while preserving application state.
// Before: Error-prone async operation
const handleSubmit = async () => {
setIsSubmitting(true);
const result = await submitData(formData);
setIsSubmitting(false); // Never executes if submitData fails
if (result.error) {
showError(result.error);
return;
}
navigateToSuccess();
};
// After: Robust error handling
const handleSubmit = async () => {
setIsSubmitting(true);
try {
const result = await submitData(formData);
if (result.error) {
showError(result.error);
return;
}
navigateToSuccess();
} catch (error) {
console.error("Submission failed:", error);
toastError({
title: "Failed to submit data",
description: error instanceof Error ? error.message : "Unknown error"
});
} finally {
setIsSubmitting(false); // Always executes, ensuring UI state is reset
}
};
This pattern prevents common issues like:
For React components fetching data, also ensure error states are properly handled in the UI:
function DataComponent() {
const { data, error, isLoading } = useSWR('/api/data');
if (error) {
return <ErrorDisplay message="Failed to load data" />;
}
if (isLoading) {
return <LoadingIndicator />;
}
return <DataDisplay data={data} />;
}
Enter the URL of a public GitHub repository