# Performance & Production

I'll never forget deploying my POS system to production for the first time. It worked perfectly on my laptop—instant renders, smooth scrolling, fast navigation. Then I tested it on a customer's old tablet over 3G. The initial load took 15 seconds. Every cart update caused a 2-second freeze. The product list re-rendered everything when I clicked "Add to Cart". It was unusable.

That's when I learned: **performance doesn't just happen**. You have to measure, identify bottlenecks, and optimize strategically. I added `React.memo` to prevent unnecessary renders, `useMemo` to cache expensive calculations, code splitting to reduce bundle size, and proper loading strategies. The result? Initial load: 2 seconds. Cart updates: instant. The app felt native.

## Measuring Performance First

**Never optimize blindly.** Measure first, then optimize what matters.

### React DevTools Profiler

1. Install React DevTools browser extension
2. Open DevTools → Profiler tab
3. Click record, interact with your app, stop recording
4. See which components rendered and how long they took

**In the POS**, I discovered:

* `ProductList` re-rendered every time cart updated (unnecessary!)
* Cart total calculation ran on every render (expensive!)
* Every `ProductCard` re-rendered when one was clicked

### Chrome DevTools Performance Tab

1. Open Chrome DevTools → Performance
2. Record while interacting
3. Look for long tasks (yellow blocks)
4. Find scripting time, rendering time

**Use this to find**:

* Slow network requests
* Heavy computations
* Large re-renders

## useMemo: Caching Expensive Calculations

**Problem**: Calculations run on every render, even when inputs haven't changed.

### Without useMemo (Slow)

```typescript
// CartSummary.tsx
import { useCart } from '../hooks/useCart';

function CartSummary() {
  const { cart } = useCart();
  
  // ❌ Recalculates on EVERY render, even if cart hasn't changed
  const subtotal = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
  const tax = subtotal * 0.05;
  const discount = calculateComplexDiscount(cart); // Expensive function!
  const total = subtotal + tax - discount;
  
  return (
    <div>
      <p>Subtotal: {subtotal} MMK</p>
      <p>Tax: {tax} MMK</p>
      <p>Discount: {discount} MMK</p>
      <p>Total: {total} MMK</p>
    </div>
  );
}
```

**Problem**: Even if you just click a button that doesn't change the cart, all these calculations run again.

### With useMemo (Fast)

```typescript
import { useMemo } from 'react';
import { useCart } from '../hooks/useCart';

function CartSummary() {
  const { cart } = useCart();
  
  // ✅ Only recalculates when cart changes
  const { subtotal, tax, discount, total } = useMemo(() => {
    const subtotal = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
    const tax = subtotal * 0.05;
    const discount = calculateComplexDiscount(cart);
    const total = subtotal + tax - discount;
    
    return { subtotal, tax, discount, total };
  }, [cart]); // Only recalculate when cart changes
  
  return (
    <div>
      <p>Subtotal: {subtotal} MMK</p>
      <p>Tax: {tax} MMK</p>
      <p>Discount: {discount} MMK</p>
      <p>Total: {total} MMK</p>
    </div>
  );
}
```

**Now**: Calculations only run when `cart` changes, not on every render.

### Real Example: Complex Product Filtering

```typescript
// ProductsPage.tsx
import { useMemo } from 'react';

function ProductsPage() {
  const { products } = useProducts();
  const [searchTerm, setSearchTerm] = useState('');
  const [selectedCategory, setSelectedCategory] = useState('all');
  const [priceRange, setPriceRange] = useState({ min: 0, max: Infinity });
  
  // ✅ Expensive filtering only runs when dependencies change
  const filteredProducts = useMemo(() => {
    return products.filter(product => {
      const matchesSearch = product.name.toLowerCase().includes(searchTerm.toLowerCase());
      const matchesCategory = selectedCategory === 'all' || product.category === selectedCategory;
      const matchesPrice = product.price >= priceRange.min && product.price <= priceRange.max;
      
      return matchesSearch && matchesCategory && matchesPrice;
    });
  }, [products, searchTerm, selectedCategory, priceRange]);
  
  // Sort is also expensive
  const sortedProducts = useMemo(() => {
    return [...filteredProducts].sort((a, b) => a.price - b.price);
  }, [filteredProducts]);
  
  return (
    <div>
      <input value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} />
      {/* ... */}
      {sortedProducts.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}
```

## useCallback: Stable Function References

**Problem**: Functions created in render are new objects every time, causing child re-renders.

### Without useCallback (Causes Re-renders)

```typescript
// ProductList.tsx
function ProductList() {
  const { products } = useProducts();
  const { addToCart } = useCart();
  
  // ❌ New function on every render
  const handleAddToCart = (product: Product) => {
    addToCart(product);
    console.log('Added to cart:', product.name);
  };
  
  return (
    <div>
      {products.map(product => (
        <ProductCard 
          key={product.id}
          product={product}
          onAdd={handleAddToCart} // New reference every time!
        />
      ))}
    </div>
  );
}
```

**Problem**: Even if `ProductCard` is memoized, it re-renders because `onAdd` is a new function every time.

### With useCallback (Stable Reference)

```typescript
import { useCallback } from 'react';

function ProductList() {
  const { products } = useProducts();
  const { addToCart } = useCart();
  
  // ✅ Same function reference across renders
  const handleAddToCart = useCallback((product: Product) => {
    addToCart(product);
    console.log('Added to cart:', product.name);
  }, [addToCart]); // Only recreate if addToCart changes
  
  return (
    <div>
      {products.map(product => (
        <ProductCard 
          key={product.id}
          product={product}
          onAdd={handleAddToCart} // Same reference!
        />
      ))}
    </div>
  );
}
```

### Real Example: Event Handlers

```typescript
function CheckoutPage() {
  const { cart, updateQuantity, removeFromCart } = useCart();
  const [couponCode, setCouponCode] = useState('');
  
  // ✅ Stable callbacks
  const handleQuantityChange = useCallback((productId: string, quantity: number) => {
    if (quantity < 1) return;
    updateQuantity(productId, quantity);
  }, [updateQuantity]);
  
  const handleRemove = useCallback((productId: string) => {
    if (window.confirm('Remove this item?')) {
      removeFromCart(productId);
    }
  }, [removeFromCart]);
  
  const handleApplyCoupon = useCallback(async () => {
    try {
      const response = await fetch('/api/coupons/validate', {
        method: 'POST',
        body: JSON.stringify({ code: couponCode })
      });
      // Handle response
    } catch (error) {
      console.error('Invalid coupon');
    }
  }, [couponCode]);
  
  return (
    <div>
      {cart.map(item => (
        <CartItem 
          key={item.productId}
          item={item}
          onQuantityChange={handleQuantityChange}
          onRemove={handleRemove}
        />
      ))}
      
      <input value={couponCode} onChange={(e) => setCouponCode(e.target.value)} />
      <button onClick={handleApplyCoupon}>Apply Coupon</button>
    </div>
  );
}
```

## React.memo: Preventing Component Re-renders

**Problem**: Child components re-render even when their props haven't changed.

### Without React.memo (Re-renders Always)

```typescript
// ProductCard.tsx
function ProductCard({ product, onAdd }: ProductCardProps) {
  console.log('ProductCard rendered:', product.name);
  
  return (
    <div className="product-card">
      <h3>{product.name}</h3>
      <p>{product.price} MMK</p>
      <button onClick={() => onAdd(product)}>Add to Cart</button>
    </div>
  );
}

export default ProductCard;
```

**Problem**: When you add ONE product to cart, ALL `ProductCard` components re-render.

### With React.memo (Re-renders Only When Props Change)

```typescript
import React from 'react';

interface ProductCardProps {
  product: Product;
  onAdd: (product: Product) => void;
}

const ProductCard = React.memo(function ProductCard({ product, onAdd }: ProductCardProps) {
  console.log('ProductCard rendered:', product.name);
  
  return (
    <div className="product-card">
      <h3>{product.name}</h3>
      <p>{product.price} MMK</p>
      <button onClick={() => onAdd(product)}>Add to Cart</button>
    </div>
  );
});

export default ProductCard;
```

**Now**: Only re-renders when `product` or `onAdd` change. If you add a different product to cart, this card doesn't re-render.

### Custom Comparison Function

```typescript
// Only re-render if product.id or product.stock changed
const ProductCard = React.memo(
  ProductCard,
  (prevProps, nextProps) => {
    return (
      prevProps.product.id === nextProps.product.id &&
      prevProps.product.stock === nextProps.product.stock
    );
  }
);
```

### Real Example: Optimized Product List

```typescript
// ProductList.tsx
import React, { useCallback } from 'react';
import { useProducts } from '../hooks/useProducts';
import { useCart } from '../hooks/useCart';

function ProductList() {
  const { products } = useProducts();
  const { addToCart } = useCart();
  
  // Stable function reference
  const handleAddToCart = useCallback((product: Product) => {
    addToCart(product);
  }, [addToCart]);
  
  return (
    <div className="product-list">
      {products.map(product => (
        <ProductCard 
          key={product.id}
          product={product}
          onAdd={handleAddToCart}
        />
      ))}
    </div>
  );
}

// Memoized component
const ProductCard = React.memo(function ProductCard({ 
  product, 
  onAdd 
}: {
  product: Product;
  onAdd: (product: Product) => void;
}) {
  return (
    <div className="product-card">
      <img src={product.imageUrl} alt={product.name} />
      <h3>{product.name}</h3>
      <p>{product.price.toLocaleString()} MMK</p>
      <p>Stock: {product.stock}</p>
      <button onClick={() => onAdd(product)}>Add to Cart</button>
    </div>
  );
});

export default ProductList;
```

**Result**: Adding one product to cart only re-renders that one card + cart component, not all 100 product cards.

## When NOT to Optimize

**Don't optimize prematurely!** These techniques add complexity.

### Don't Use useMemo for Simple Calculations

```typescript
// ❌ Overkill - simple calculation
const total = useMemo(() => price * quantity, [price, quantity]);

// ✅ Just calculate directly
const total = price * quantity;
```

### Don't Memo Every Component

```typescript
// ❌ Unnecessary - component only renders once
const Header = React.memo(function Header() {
  return <h1>POS System</h1>;
});

// ✅ Simple component, no need
function Header() {
  return <h1>POS System</h1>;
}
```

### Only Optimize When You See Problems

1. **Measure** with Profiler
2. **Identify** slow components
3. **Optimize** those specific components
4. **Measure again** to verify improvement

**Golden Rule**: Premature optimization is the root of all evil. Profile first, optimize what matters.

## Code Splitting with lazy() and Suspense

**Problem**: Initial JavaScript bundle is huge. Users download code for admin panel even if they never go there.

### Without Code Splitting (Large Bundle)

```typescript
// App.tsx
import ProductsPage from './pages/ProductsPage';
import CartPage from './pages/CartPage';
import CheckoutPage from './pages/CheckoutPage';
import OrdersPage from './pages/OrdersPage';
import AdminPage from './pages/AdminPage'; // Huge component users rarely visit!

// All code included in initial bundle
```

### With Code Splitting (Small Initial Bundle)

```typescript
// App.tsx
import React, { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

// Eager loading (included in main bundle)
import Layout from './components/Layout';
import ProductsPage from './pages/ProductsPage';

// Lazy loading (separate chunks)
const CartPage = lazy(() => import('./pages/CartPage'));
const CheckoutPage = lazy(() => import('./pages/CheckoutPage'));
const OrdersPage = lazy(() => import('./pages/OrdersPage'));
const AdminPage = lazy(() => import('./pages/AdminPage'));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>Loading...</div>}>
        <Routes>
          <Route path="/" element={<Layout />}>
            <Route index element={<ProductsPage />} />
            <Route path="cart" element={<CartPage />} />
            <Route path="checkout" element={<CheckoutPage />} />
            <Route path="orders" element={<OrdersPage />} />
            <Route path="admin" element={<AdminPage />} />
          </Route>
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

export default App;
```

**Now**:

* Initial load: ProductsPage + shared code
* User visits `/cart`: Downloads CartPage chunk
* User visits `/admin`: Downloads AdminPage chunk

**Bundle sizes in my POS**:

* Before code splitting: 450 KB initial
* After: 180 KB initial, 50 KB per route

### Better Loading UI

```typescript
// components/RouteLoader.tsx
function RouteLoader() {
  return (
    <div className="route-loader">
      <div className="spinner" />
      <p>Loading page...</p>
    </div>
  );
}

// App.tsx
<Suspense fallback={<RouteLoader />}>
  <Routes>
    {/* ... */}
  </Routes>
</Suspense>
```

## Production Build Optimizations

### Environment Variables

```typescript
// .env.production
REACT_APP_API_URL=https://api.production.com
REACT_APP_STRIPE_KEY=pk_live_...
```

```typescript
// config.ts
export const config = {
  apiUrl: process.env.REACT_APP_API_URL || 'http://localhost:3000',
  stripeKey: process.env.REACT_APP_STRIPE_KEY,
  isDevelopment: process.env.NODE_ENV === 'development'
};

// Usage
import { config } from './config';

fetch(`${config.apiUrl}/products`);
```

### Remove Console Logs in Production

```typescript
// utils/logger.ts
export const logger = {
  log: (...args: any[]) => {
    if (process.env.NODE_ENV === 'development') {
      console.log(...args);
    }
  },
  error: (...args: any[]) => {
    // Always log errors
    console.error(...args);
  }
};

// Usage
import { logger } from './utils/logger';

logger.log('Debug info'); // Only in dev
logger.error('Error occurred'); // In dev and prod
```

### Error Boundaries

Catch runtime errors gracefully:

```typescript
// components/ErrorBoundary.tsx
import React, { Component, ReactNode } from 'react';

interface Props {
  children: ReactNode;
}

interface State {
  hasError: boolean;
  error: Error | null;
}

class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false, error: null };
  }
  
  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error };
  }
  
  componentDidCatch(error: Error, errorInfo: any) {
    console.error('Error caught by boundary:', error, errorInfo);
    
    // Send to error tracking service
    if (process.env.NODE_ENV === 'production') {
      // Sentry, LogRocket, etc.
    }
  }
  
  render() {
    if (this.state.hasError) {
      return (
        <div className="error-page">
          <h1>Something went wrong</h1>
          <p>We've been notified and are working on a fix.</p>
          <button onClick={() => window.location.reload()}>
            Reload Page
          </button>
        </div>
      );
    }
    
    return this.props.children;
  }
}

export default ErrorBoundary;
```

```typescript
// App.tsx
import ErrorBoundary from './components/ErrorBoundary';

function App() {
  return (
    <ErrorBoundary>
      <BrowserRouter>
        {/* ... */}
      </BrowserRouter>
    </ErrorBoundary>
  );
}
```

## Deployment

### Building for Production

```bash
npm run build
```

Creates optimized build in `build/` folder:

* Minified JavaScript
* Optimized CSS
* Asset optimization
* Source maps (for debugging)

### Deploy to Vercel

1. Install Vercel CLI:

```bash
npm install -g vercel
```

2. Deploy:

```bash
vercel
```

3. Follow prompts, done!

**Vercel automatically**:

* Detects React app
* Runs `npm run build`
* Deploys to CDN
* Provides HTTPS
* Gives you a URL

### Deploy to Netlify

1. Build your app:

```bash
npm run build
```

2. Drag `build/` folder to Netlify

Or use Netlify CLI:

```bash
npm install -g netlify-cli
netlify deploy --prod --dir=build
```

### Deploy to Custom Server (Nginx)

```nginx
# /etc/nginx/sites-available/pos-system
server {
    listen 80;
    server_name pos.example.com;
    
    root /var/www/pos-system/build;
    index index.html;
    
    # SPA fallback
    location / {
        try_files $uri $uri/ /index.html;
    }
    
    # Cache static assets
    location /static/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}
```

## Complete Production Checklist

Before deploying to production, verify:

### Performance

* [ ] Code splitting for routes
* [ ] Lazy loading for heavy components
* [ ] Images optimized (WebP, compressed)
* [ ] Bundle size < 250 KB (initial)
* [ ] Lighthouse score > 90

### Functionality

* [ ] All routes work
* [ ] Protected routes redirect properly
* [ ] Error handling for all API calls
* [ ] Loading states everywhere
* [ ] Forms validate correctly

### Security

* [ ] No sensitive data in code
* [ ] Environment variables for secrets
* [ ] HTTPS enabled
* [ ] Authentication working
* [ ] XSS protection (don't use dangerouslySetInnerHTML)

### User Experience

* [ ] Loading indicators
* [ ] Error messages are helpful
* [ ] Works on mobile
* [ ] Works on slow connections
* [ ] Offline fallback (optional)

### Monitoring

* [ ] Error tracking (Sentry)
* [ ] Analytics (Google Analytics)
* [ ] Performance monitoring
* [ ] Console logs removed

## Real POS Performance Results

Here's what I achieved in the production POS system:

**Before Optimization**:

* Initial bundle: 450 KB
* Products page load: 3.2s
* Cart update: 800ms (visible lag)
* Lighthouse score: 62

**After Optimization**:

* Initial bundle: 180 KB (60% reduction)
* Products page load: 1.1s (66% faster)
* Cart update: 50ms (94% faster)
* Lighthouse score: 94

**Optimizations Applied**:

1. Code splitting (saved 270 KB initial)
2. React.memo on ProductCard (eliminated 100+ re-renders)
3. useMemo for cart calculations
4. useCallback for event handlers
5. Image optimization (WebP, lazy loading)
6. Debounced search input

## Key Learnings

1. **Measure before optimizing** - use React Profiler
2. **useMemo** caches expensive calculations
3. **useCallback** provides stable function references
4. **React.memo** prevents unnecessary re-renders
5. **Don't optimize prematurely** - profile first
6. **Code splitting** reduces initial bundle size
7. **lazy() + Suspense** for route-based splitting
8. **Error boundaries** catch runtime errors
9. **Build and test** production builds locally
10. **Monitor performance** in production

## Final POS System Architecture

Here's the complete, production-ready structure:

```
pos-frontend/
├── src/
│   ├── components/
│   │   ├── ErrorBoundary.tsx
│   │   ├── Layout.tsx
│   │   ├── Navigation.tsx
│   │   ├── ProductCard.tsx (React.memo)
│   │   ├── CartItem.tsx (React.memo)
│   │   └── ProtectedRoute.tsx
│   ├── contexts/
│   │   ├── AuthContext.tsx
│   │   └── CartContext.tsx
│   ├── hooks/
│   │   ├── useProducts.ts
│   │   ├── useCart.ts
│   │   ├── useAuth.ts
│   │   ├── useDebounce.ts
│   │   └── useLocalStorage.ts
│   ├── pages/
│   │   ├── ProductsPage.tsx
│   │   ├── CartPage.tsx (lazy)
│   │   ├── CheckoutPage.tsx (lazy)
│   │   ├── OrdersPage.tsx (lazy)
│   │   └── AdminPage.tsx (lazy)
│   ├── utils/
│   │   ├── config.ts
│   │   └── logger.ts
│   ├── App.tsx
│   └── index.tsx
├── .env.production
├── package.json
└── tsconfig.json
```

## Congratulations!

You've built a production-ready React application from scratch. You understand:

* Components and props
* State and effects
* Component communication
* Context API
* Custom hooks
* Routing
* Performance optimization
* Production deployment

The POS system we built together demonstrates real-world patterns you'll use in every React app. You're ready to build production applications.

## Next Steps

Keep learning:

* **State management**: Redux, Zustand, Jotai
* **Server state**: React Query, SWR
* **Testing**: Jest, React Testing Library, Playwright
* **Advanced patterns**: Render props, compound components, HOCs
* **TypeScript**: Advanced types, generics
* **Next.js**: Server-side rendering, static generation
* **React Native**: Build mobile apps with React

Build projects, read code, and never stop learning. Happy coding! 🚀
