Error Handling

Learn how to handle errors effectively in qortex. Best practices for error states, retry logic, and fallback data.

Error Handling Patterns

Basic Error Handling

Handle errors with proper loading and error states

function TodosList() {
  const { data, isLoading, error, refetch } = useQuery(["todos"]);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <button onClick={() => refetch()}>Refresh</button>
      <ul>
        {data?.map(todo => (
          <li key={todo.id}>{todo.title}</li>
        ))}
      </ul>
    </div>
  );
}

Graceful Degradation

Show placeholder data while handling errors gracefully

function ProductList() {
  const { data: products, error } = useQuery(["products"], {
    placeholderData: [], // Show empty list while loading
    usePlaceholderOnError: true // Show empty list on error
  });

  return (
    <div>
      {error && (
        <div className="error-banner">
          āš ļø Some products couldn't be loaded, showing cached data
        </div>
      )}
      {products?.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

Retry Logic

Implement retry logic with exponential backoff

function DataComponent() {
  const [retryCount, setRetryCount] = useState(0);
  
  const { data, error, refetch } = useQuery(["data"], {
    enabled: retryCount < 3 // Stop retrying after 3 attempts
  });

  const handleRetry = async () => {
    setRetryCount(prev => prev + 1);
    await refetch();
  };

  if (error && retryCount < 3) {
    return (
      <div>
        <p>āŒ Something went wrong: {error.message}</p>
        <button onClick={handleRetry}>
          šŸ”„ Try Again ({retryCount}/3)
        </button>
      </div>
    );
  }

  return <div>{/* Your content */}</div>;
}

Fallback Data

Provide meaningful fallback data for better UX

function UserProfile({ userId }: { userId: string }) {
  const { data: user } = useQuery(["user", userId], {
    placeholderData: {
      name: "Loading...",
      avatar: "/default-avatar.png",
      bio: "User information is being loaded..."
    },
    usePlaceholderOnError: true
  });

  return (
    <div>
      <img src={user.avatar} alt={user.name} />
      <h2>{user.name}</h2>
      <p>{user.bio}</p>
    </div>
  );
}

Best Practices

āœ… Do

  • • Always provide meaningful error messages
  • • Use placeholder data for better UX
  • • Implement retry logic with limits
  • • Show loading states during retries
  • • Log errors for debugging
  • • Handle network connectivity issues

āŒ Don't

  • • Show technical error messages to users
  • • Retry indefinitely without limits
  • • Ignore error states in UI
  • • Leave users without feedback
  • • Forget to handle edge cases
  • • Expose sensitive information in errors

Error Handling Configuration

Global Configuration

// Set global error handling defaults
setDefaultConfig({
  usePreviousDataOnError: true, // Keep previous data on error
  usePlaceholderOnError: false, // Don't use placeholder on error
  staleTime: 5 * 60 * 1000, // 5 minutes stale time
});

// Per-query error handling
registerFetcher(["critical-data"], {
  fetcher: async () => {
    const response = await fetch("/api/critical-data");
    if (!response.ok) throw new Error('Failed to fetch critical data');
    return response.json();
  },
  placeholderData: { status: 'loading' },
  usePlaceholderOnError: true // Override global setting
});

Ready to Handle Errors?

Now that you understand error handling patterns, explore more advanced features and configuration options.