SDK Best Practices
Follow these best practices to build high-performance, maintainable plugins with the TagadaPay SDK.Quick Links:
- Initialize Checkout Sessions - Create checkouts programmatically
- API Reference - Complete hook documentation
- Examples - Real-world implementations
Performance Optimization
1. Use React.memo for Heavy Components
Copy
import React, { memo } from 'react';
import { formatMoney } from '@tagadapay/plugin-sdk';
const ProductCard = memo(({ product, currency }) => {
return (
<div className="product-card">
<h3>{product.name}</h3>
<p>{formatMoney(product.price, currency)}</p>
</div>
);
});
export default ProductCard;
2. Optimize Bundle Size
Copy
// ✅ Import only what you need
import { useCheckout, formatMoney } from '@tagadapay/plugin-sdk';
// ❌ Don't import the entire SDK
import * as TagadaSDK from '@tagadapay/plugin-sdk';
3. Lazy Load Components
Copy
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function MyPlugin() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
State Management
1. Use SDK Hooks Correctly
Copy
// ✅ Good: Use hooks at component level
function CheckoutComponent() {
const { checkout, updateCheckout } = useCheckout();
return <div>{/* component JSX */}</div>;
}
// ❌ Avoid: Don't call hooks conditionally
function BadComponent({ showCheckout }) {
if (showCheckout) {
const { checkout } = useCheckout(); // This will cause errors
}
}
2. Minimize Re-renders
Copy
import { useMemo } from 'react';
import { useCheckout } from '@tagadapay/plugin-sdk';
function OptimizedComponent() {
const { checkout } = useCheckout();
// Memoize expensive calculations
const totalWithTax = useMemo(() => {
return checkout.total + checkout.tax;
}, [checkout.total, checkout.tax]);
return <div>Total: {totalWithTax}</div>;
}
Error Handling
1. Handle Async Operations Properly
Copy
import { useState } from 'react';
import { useCheckout, trackEvent } from '@tagadapay/plugin-sdk';
function PaymentButton() {
const { pay } = useCheckout();
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const handlePayment = async () => {
setLoading(true);
setError(null);
try {
await pay();
trackEvent('payment_success');
} catch (err) {
setError(err.message);
trackEvent('payment_error', { error: err.message });
} finally {
setLoading(false);
}
};
return (
<div>
<button onClick={handlePayment} disabled={loading}>
{loading ? 'Processing...' : 'Pay Now'}
</button>
{error && <div className="error">{error}</div>}
</div>
);
}
2. Provide Fallbacks
Copy
import { useStore } from '@tagadapay/plugin-sdk';
function BrandedComponent() {
const { branding } = useStore();
// Provide fallback values
const primaryColor = branding?.primaryColor || '#3b82f6';
const fontFamily = branding?.fontFamily || 'system-ui, sans-serif';
return (
<div style={{ color: primaryColor, fontFamily }}>
Content with fallback styling
</div>
);
}
User Experience
1. Show Loading States
Copy
function CheckoutForm() {
const { checkout, updateCustomer, loading } = useCheckout();
if (loading) {
return (
<div className="loading-state">
<div className="spinner" />
<p>Loading your checkout...</p>
</div>
);
}
return <form>{/* checkout form */}</form>;
}
2. Provide Clear Feedback
Copy
import { useState } from 'react';
import { usePromotionCodes } from '@tagadapay/plugin-sdk';
function PromoCodeInput() {
const { applyCode } = usePromotionCodes();
const [code, setCode] = useState('');
const [status, setStatus] = useState('idle'); // idle, applying, success, error
const handleApply = async () => {
setStatus('applying');
try {
await applyCode(code);
setStatus('success');
setCode('');
} catch (error) {
setStatus('error');
}
};
return (
<div>
<input
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder="Enter promo code"
/>
<button onClick={handleApply} disabled={status === 'applying'}>
{status === 'applying' ? 'Applying...' : 'Apply'}
</button>
{status === 'success' && (
<div className="success">✅ Promo code applied!</div>
)}
{status === 'error' && (
<div className="error">❌ Invalid promo code</div>
)}
</div>
);
}
Accessibility
1. Use Semantic HTML
Copy
function AccessibleForm() {
return (
<form>
<fieldset>
<legend>Contact Information</legend>
<label htmlFor="email">
Email Address *
<input
id="email"
type="email"
required
aria-describedby="email-error"
/>
</label>
<div id="email-error" role="alert">
{/* Error message */}
</div>
</fieldset>
</form>
);
}
2. Provide Keyboard Navigation
Copy
function KeyboardFriendlyComponent() {
const handleKeyDown = (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleAction();
}
};
return (
<div
role="button"
tabIndex={0}
onKeyDown={handleKeyDown}
onClick={handleAction}
>
Interactive Element
</div>
);
}
Security
1. Sanitize User Input
Copy
import DOMPurify from 'dompurify';
function UserContentDisplay({ userContent }) {
const sanitizedContent = DOMPurify.sanitize(userContent);
return (
<div dangerouslySetInnerHTML={{ __html: sanitizedContent }} />
);
}
2. Handle Sensitive Data Carefully
Copy
// ✅ Good: Don't log sensitive data
function PaymentComponent() {
const handlePayment = (paymentData) => {
console.log('Processing payment...'); // Don't log payment details
// Process payment
};
}
// ❌ Avoid: Logging sensitive information
function BadPaymentComponent() {
const handlePayment = (paymentData) => {
console.log('Payment data:', paymentData); // This could expose sensitive info
};
}
Testing
1. Test with Different Store Configurations
Copy
// Test with various store setups
const testStores = [
{ currency: 'USD', locale: 'en-US' },
{ currency: 'EUR', locale: 'de-DE' },
{ currency: 'JPY', locale: 'ja-JP' }
];
testStores.forEach(store => {
test(`works with ${store.currency} currency`, () => {
// Test your component with different store configs
});
});
2. Mock SDK Dependencies
Copy
// __mocks__/@tagadapay/plugin-sdk.js
export const useCheckout = jest.fn(() => ({
checkout: {
total: 2999,
currency: 'USD',
items: []
},
updateCheckout: jest.fn(),
pay: jest.fn()
}));
export const formatMoney = jest.fn((amount, currency) =>
`${currency} ${(amount / 100).toFixed(2)}`
);
Code Organization
1. Use Custom Hooks
Copy
// hooks/useFormValidation.ts
import { useState, useCallback } from 'react';
export function useFormValidation(validationRules) {
const [errors, setErrors] = useState({});
const validate = useCallback((data) => {
const newErrors = {};
Object.keys(validationRules).forEach(field => {
const rule = validationRules[field];
if (!rule(data[field])) {
newErrors[field] = `${field} is invalid`;
}
});
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
}, [validationRules]);
return { errors, validate };
}
2. Separate Business Logic
Copy
// utils/checkoutHelpers.ts
export function calculateTotal(items, tax = 0, shipping = 0) {
const subtotal = items.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
);
return subtotal + tax + shipping;
}
export function validateEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
// components/CheckoutForm.tsx
import { calculateTotal, validateEmail } from '../utils/checkoutHelpers';
Configuration Management
1. Use Environment-Specific Settings
Copy
const config = {
development: {
apiUrl: 'https://app.tagadapay.dev',
debug: true
},
production: {
apiUrl: 'https://app.tagadapay.com',
debug: false
}
}[process.env.NODE_ENV || 'development'];
2. Make Components Configurable
Copy
interface PluginConfig {
showUpsells: boolean;
maxUpsellCount: number;
theme: 'light' | 'dark';
}
function ConfigurablePlugin({ config }: { config: PluginConfig }) {
return (
<div className={`theme-${config.theme}`}>
{/* Main content */}
{config.showUpsells && (
<UpsellSection maxCount={config.maxUpsellCount} />
)}
</div>
);
}
Monitoring and Analytics
1. Track Important Events
Copy
import { trackEvent } from '@tagadapay/plugin-sdk';
function TrackingComponent() {
useEffect(() => {
trackEvent('plugin_loaded', {
version: '1.2.0',
timestamp: Date.now()
});
}, []);
const handleUserAction = (action) => {
trackEvent('user_interaction', {
action,
timestamp: Date.now()
});
};
}
2. Monitor Performance
Copy
function PerformanceMonitor({ children }) {
useEffect(() => {
const startTime = performance.now();
return () => {
const loadTime = performance.now() - startTime;
trackEvent('plugin_performance', {
loadTime,
component: 'main'
});
};
}, []);
return children;
}
