SiamCafe.net Blog
Technology

Radix UI Primitives Chaos Engineering

radix ui primitives chaos engineering
Radix UI Primitives Chaos Engineering | SiamCafe Blog
2025-08-27· อ. บอม — SiamCafe.net· 9,868 คำ

Radix UI Chaos Engineering

Radix UI Primitives Chaos Engineering React Error Boundary Resilience Retry Circuit Breaker Timeout Graceful Degradation Production

Chaos TypeScenarioExpected BehaviorTool
NetworkAPI Timeout 500 OfflineError Message Retry Cached DataMSW DevTools Throttle
DataEmpty Null Large MalformedEmpty State Fallback PaginationMSW Faker.js
StateRapid Click Race ConditionDebounce Lock Consistent StatePlaywright Cypress
ResourceMemory CPU Large DOMVirtualization Lazy Load CleanupChrome DevTools Lighthouse

Error Boundary Setup

# === React Error Boundary with Radix UI ===

# // ErrorBoundary.tsx
# import { ErrorBoundary } from 'react-error-boundary';
# import * as Toast from '@radix-ui/react-toast';
#
# function ErrorFallback({ error, resetErrorBoundary }) {
#   return (
#     <div className="p-4 border border-red-500 rounded-lg">
#       <h3 className="text-red-500 font-bold">Something went wrong</h3>
#       <p className="text-sm text-gray-400">{error.message}</p>
#       <button onClick={resetErrorBoundary}
#         className="mt-2 px-4 py-2 bg-red-500 text-white rounded">
#         Try Again
#       </button>
#     </div>
#   );
# }
#
# // Granular Error Boundaries
# function Dashboard() {
#   return (
#     <div className="grid grid-cols-3 gap-4">
#       <ErrorBoundary FallbackComponent={ErrorFallback}>
#         <StatsWidget />
#       </ErrorBoundary>
#       <ErrorBoundary FallbackComponent={ErrorFallback}>
#         <ChartWidget />
#       </ErrorBoundary>
#       <ErrorBoundary FallbackComponent={ErrorFallback}>
#         <TableWidget />
#       </ErrorBoundary>
#     </div>
#   );
# }

from dataclasses import dataclass

@dataclass
class ErrorBoundaryConfig:
    scope: str
    fallback: str
    recovery: str
    logging: str

boundaries = [
    ErrorBoundaryConfig("App Level (Root)",
        "Full Page Error + Reload Button",
        "Window Reload หรือ Navigate Home",
        "Sentry captureException + User Context"),
    ErrorBoundaryConfig("Page Level",
        "Page Error Message + Back Button",
        "Navigate Back หรือ Retry Page Load",
        "Sentry + Page Route + User Action"),
    ErrorBoundaryConfig("Widget Level",
        "Small Error Card + Retry Button",
        "Reset Widget State + Refetch Data",
        "Sentry + Widget Name + Props"),
    ErrorBoundaryConfig("Dialog/Modal",
        "Close Modal + Toast Error Message",
        "Close Modal + Reset Form State",
        "Sentry + Modal Name + Form Data"),
    ErrorBoundaryConfig("Async Component (Suspense)",
        "Loading Skeleton → Error Fallback",
        "Retry Fetch + Show Stale Data",
        "Sentry + API Endpoint + Response"),
]

print("=== Error Boundary Strategy ===")
for b in boundaries:
    print(f"  [{b.scope}]")
    print(f"    Fallback: {b.fallback}")
    print(f"    Recovery: {b.recovery}")
    print(f"    Logging: {b.logging}")

Chaos Testing

# === Chaos Test Scenarios ===

# // MSW (Mock Service Worker) for Network Chaos
# import { http, HttpResponse, delay } from 'msw';
#
# export const chaosHandlers = [
#   // Timeout (10s delay)
#   http.get('/api/stats', async () => {
#     await delay(10000);
#     return HttpResponse.json({ error: 'timeout' });
#   }),
#   // 500 Error
#   http.get('/api/users', () => {
#     return new HttpResponse(null, { status: 500 });
#   }),
#   // Empty Response
#   http.get('/api/orders', () => {
#     return HttpResponse.json({ data: [], total: 0 });
#   }),
#   // Malformed JSON
#   http.get('/api/products', () => {
#     return new HttpResponse('not json', {
#       headers: { 'Content-Type': 'application/json' }
#     });
#   }),
# ];

@dataclass
class ChaosTest:
    scenario: str
    chaos_action: str
    expected_ui: str
    test_tool: str

tests = [
    ChaosTest("API Timeout (10s)",
        "MSW delay(10000) หรือ DevTools throttle",
        "Loading Skeleton → Timeout Error → Retry Button",
        "MSW + Playwright waitForTimeout"),
    ChaosTest("API 500 Error",
        "MSW return HttpResponse(null, {status: 500})",
        "Error Boundary Fallback → Retry Button",
        "MSW + Playwright expect(errorMsg).toBeVisible()"),
    ChaosTest("Empty Data",
        "MSW return {data: [], total: 0}",
        "Empty State UI (No data found)",
        "MSW + Playwright expect(emptyState).toBeVisible()"),
    ChaosTest("Rapid Click (Double Submit)",
        "Playwright click 10 times fast",
        "Button disabled after first click ป้องกัน Double Submit",
        "Playwright click({clickCount: 10})"),
    ChaosTest("Network Offline",
        "Playwright context.setOffline(true)",
        "Offline Banner + Cached Data + Auto Retry เมื่อ Online",
        "Playwright setOffline + waitForSelector"),
    ChaosTest("Large Dataset (10K rows)",
        "MSW return Array(10000).fill(row)",
        "Virtual List/Pagination ไม่ Freeze ไม่ Memory Leak",
        "Playwright + Chrome Performance Monitor"),
]

print("=== Chaos Test Scenarios ===")
for t in tests:
    print(f"  [{t.scenario}]")
    print(f"    Chaos: {t.chaos_action}")
    print(f"    Expected: {t.expected_ui}")
    print(f"    Tool: {t.test_tool}")

Resilience Patterns

# === UI Resilience Patterns ===

@dataclass
class ResiliencePattern:
    pattern: str
    implementation: str
    radix_component: str
    benefit: str

patterns = [
    ResiliencePattern("Retry with Backoff",
        "TanStack Query retry: 3 retryDelay: exponential",
        "Toast แสดง Retrying... + Progress",
        "Auto-recover จาก Transient Failure"),
    ResiliencePattern("Circuit Breaker",
        "Custom Hook ถ้า fail 5 ครั้ง หยุด 30s",
        "Alert Dialog แจ้ง Service Unavailable",
        "ป้องกัน Server Overload"),
    ResiliencePattern("Timeout + AbortController",
        "AbortController signal timeout 10s",
        "Toast แจ้ง Request Timeout + Retry",
        "ไม่ค้าง Loading ตลอดกาล"),
    ResiliencePattern("Optimistic Update",
        "TanStack Query mutate onMutate optimistic",
        "Checkbox/Toggle Update ทันที Rollback ถ้า Fail",
        "UX เร็ว ลด Perceived Latency"),
    ResiliencePattern("Graceful Degradation",
        "Feature Flag + Error Boundary per Widget",
        "Widget Error แสดง Fallback ส่วนอื่นทำงานต่อ",
        "App ไม่ Crash ทั้งหมดจาก Widget เดียว"),
    ResiliencePattern("Stale-while-revalidate",
        "TanStack Query staleTime + Cache",
        "แสดง Cached Data ทันที Background Refetch",
        "User เห็นข้อมูลทันที ไม่ต้องรอ"),
]

print("=== Resilience Patterns ===")
for p in patterns:
    print(f"  [{p.pattern}]")
    print(f"    Impl: {p.implementation}")
    print(f"    Radix: {p.radix_component}")
    print(f"    Benefit: {p.benefit}")

เคล็ดลับ

Radix UI คืออะไร

Open Source Unstyled Accessible React Primitives WAI-ARIA Keyboard Focus Composable Dialog Dropdown Popover Tooltip shadcn/ui Base

Chaos Engineering สำหรับ UI คืออะไร

จงใจทำให้ Error Network Timeout 500 Empty Data Rapid Click Offline Large Dataset Memory CPU Resource Test Resilience

Error Boundary ตั้งอย่างไร

react-error-boundary Granular App Page Widget Dialog Fallback Recovery Retry Reset Sentry Logging componentDidCatch Suspense

Resilience Pattern มีอะไร

Retry Backoff Circuit Breaker Timeout AbortController Optimistic Graceful Degradation Stale-while-revalidate TanStack Query Cache

สรุป

Radix UI Primitives Chaos Engineering Error Boundary MSW Resilience Retry Circuit Breaker Timeout Optimistic Graceful TanStack Production

📖 บทความที่เกี่ยวข้อง

Radix UI Primitives Blue Green Canary Deployอ่านบทความ → Radix UI Primitives Learning Path Roadmapอ่านบทความ → AWS Glue ETL Chaos Engineeringอ่านบทความ → Radix UI Primitives Scaling Strategy วิธี Scaleอ่านบทความ → Radix UI Primitives Freelance IT Careerอ่านบทความ →

📚 ดูบทความทั้งหมด →