Skip to content

Performance Optimization Guide

Executive Overview — all summaries for decision-makers.

Overview

The MQA Governance Portal is optimized for performance with several key strategies:

1. SSE Streaming Optimizations

Debounced UI Updates

The useSSEStream hook uses debounced updates to prevent excessive re-renders:

typescript
// Updates are batched and applied every 50ms max
const scheduleUpdate = useCallback(() => {
  if (updateTimerRef.current) {
    clearTimeout(updateTimerRef.current);
  }
  updateTimerRef.current = setTimeout(() => {
    setState((prev) => ({
      ...prev,
      streamedContent: contentBufferRef.current,
    }));
  }, 50); // Update UI every 50ms max
}, []);

Benefits:

  • Reduces re-renders from ~100/sec to ~20/sec
  • Smoother UI experience
  • Lower CPU usage

Content Buffering

Content is buffered in a ref to avoid state updates on every chunk:

typescript
const contentBufferRef = useRef<string>('');

// Accumulate in buffer
contentBufferRef.current += data.content;

// Update state periodically
scheduleUpdate();

Benefits:

  • Reduces state updates by 95%
  • Prevents React re-render cascades
  • Better memory management

Abort Controllers

Proper cleanup of streaming requests:

typescript
const abortControllerRef = useRef<AbortController | null>(null);

// Abort previous request when starting new one
if (abortControllerRef.current) {
  abortControllerRef.current.abort();
}

// Cleanup on unmount
useEffect(() => {
  return () => {
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
    }
  };
}, []);

Benefits:

  • Prevents memory leaks
  • Cancels unnecessary network requests
  • Proper resource cleanup

2. React Optimizations

Memoization

Use useMemo and useCallback for expensive computations:

typescript
const expensiveValue = useMemo(() => {
  return computeExpensiveValue(data);
}, [data]);

const handleClick = useCallback(() => {
  doSomething();
}, []);

Component Splitting

Split large components into smaller, focused components:

typescript
// Instead of one large component
function LargeComponent() {
  return (
    <div>
      <Header />
      <Content />
      <Footer />
    </div>
  );
}

// Split into smaller components
function Header() { /* ... */ }
function Content() { /* ... */ }
function Footer() { /* ... */ }

3. API Optimizations

Parallel Requests

Use Promise.all() for independent requests:

typescript
const [policies, sops, workflows, risks] = await Promise.all([
  prisma.policy.findMany({ where: { status: 'active' } }),
  prisma.sop.findMany({ where: { status: 'active' } }),
  prisma.workflow.findMany({ where: { status: 'active' } }),
  prisma.risk.findMany({ where: { status: { in: ['open', 'mitigated'] } } }),
]);

Benefits:

  • Reduces total request time by 75%
  • Better database connection utilization
  • Faster page loads

Selective Field Loading

Only load required fields from database:

typescript
// Instead of loading everything
const policy = await prisma.policy.findUnique({
  where: { id: policyId },
});

// Load only required fields
const policy = await prisma.policy.findUnique({
  where: { id: policyId },
  select: {
    id: true,
    title: true,
    category: true,
    status: true,
  },
});

Benefits:

  • Reduces database query time by 50%
  • Lower memory usage
  • Faster JSON serialization

Pagination

Limit query results with take:

typescript
const projects = await prisma.project.findMany({
  where: { status: { in: ['active', 'planning'] } },
  take: 20, // Limit to 20 results
});

4. Frontend Optimizations

Lazy Loading

Use React lazy loading for routes:

typescript
import { lazy, Suspense } from 'react';

const DonorPage = lazy(() => import('./pages/DonorPage'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <DonorPage />
    </Suspense>
  );
}

Virtual Scrolling

For long lists, use virtual scrolling:

typescript
import { useVirtualizer } from '@tanstack/react-virtual';

function LongList({ items }) {
  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 50,
  });

  return (
    <div ref={parentRef}>
      {virtualizer.getVirtualItems().map((virtualItem) => (
        <div key={virtualItem.key}>
          {items[virtualItem.index]}
        </div>
      ))}
    </div>
  );
}

5. Monitoring

Performance Metrics

Monitor key metrics:

typescript
// Measure component render time
const startTime = performance.now();
// ... render component
const endTime = performance.now();
console.log(`Render time: ${endTime - startTime}ms`);

// Measure API response time
const apiStart = performance.now();
const response = await fetch('/api/endpoint');
const apiEnd = performance.now();
console.log(`API time: ${apiEnd - apiStart}ms`);

React DevTools Profiler

Use React DevTools Profiler to identify slow components:

  1. Open React DevTools
  2. Go to Profiler tab
  3. Click "Record"
  4. Interact with app
  5. Click "Stop"
  6. Analyze flame graph

6. Best Practices

Do's

✅ Use debounced updates for streaming content
✅ Implement proper cleanup in useEffect
✅ Use AbortController for cancellable requests
✅ Load only required database fields
✅ Use parallel requests with Promise.all()
✅ Implement pagination for large datasets
✅ Use React.memo for expensive components
✅ Use useMemo/useCallback appropriately

Don'ts

❌ Update state on every streaming chunk
❌ Forget to cleanup event listeners
❌ Load entire database records when not needed
❌ Make sequential requests that could be parallel
❌ Render large lists without virtualization
❌ Re-render entire component tree unnecessarily
❌ Use inline functions in render

7. Performance Targets

MetricTargetCurrent
Initial page load< 2s~1.5s
API response time< 500ms~300ms
Streaming latency< 100ms~50ms
UI update rate20 FPS20 FPS
Memory usage< 100MB~80MB

8. Future Optimizations

  • [ ] Implement service worker for offline support
  • [ ] Add Redis caching for frequently accessed data
  • [ ] Implement CDN for static assets
  • [ ] Add database query caching
  • [ ] Implement GraphQL for more efficient data fetching