Part 6: Advanced Patterns, Optimization & Testing

Welcome to our most advanced installment yet! Today we’ll unlock professional-grade React techniques used in production apps at top companies.

Advanced Component Patterns

1. Compound Components

function Accordion({ children }) {
  const [activeIndex, setActiveIndex] = useState(null);
  
  return React.Children.map(children, (child, index) =>
    React.cloneElement(child, {
      isActive: index === activeIndex,
      onToggle: () => setActiveIndex(index === activeIndex ? null : index)
    })
  );
}

function AccordionItem({ isActive, onToggle, children }) {
  return (
    <div className="accordion-item">
      <button onClick={onToggle}>
        {children[0]}
        <span>{isActive ? '−' : '+'}</span>
      </button>
      {isActive && children[1]}
    </div>
  );
}

// Usage:
<Accordion>
  <AccordionItem>
    <h3>Section 1</h3>
    <div>Content 1</div>
  </AccordionItem>
  <AccordionItem>
    <h3>Section 2</h3>
    <div>Content 2</div>
  </AccordionItem>
</Accordion>

2. Render Props Pattern

function MouseTracker({ render }) {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  const handleMouseMove = (e) => {
    setPosition({ x: e.clientX, y: e.clientY });
  };

  return (
    <div onMouseMove={handleMouseMove}>
      {render(position)}
    </div>
  );
}

// Usage:
<MouseTracker
  render={({ x, y }) => (
    <div>
      Mouse at: {x}, {y}
    </div>
  )}
/>

Performance Optimization

1. React.memo for Component Memoization

const ExpensiveComponent = React.memo(function ({ data }) {
  // Heavy computations here
  return <div>{processedData}</div>;
}, (prevProps, nextProps) => {
  // Custom comparison (optional)
  return prevProps.data.id === nextProps.data.id;
});

2. useMemo/useCallback Optimization

function ProductList({ products, searchTerm }) {
  const filteredProducts = useMemo(() => {
    return products.filter(product =>
      product.name.toLowerCase().includes(searchTerm.toLowerCase())
    );
  }, [products, searchTerm]);

  const handleAddToCart = useCallback((productId) => {
    // Optimized callback
  }, [cart]);

  return (
    <ul>
      {filteredProducts.map(product => (
        <ProductItem 
          key={product.id}
          product={product}
          onAddToCart={handleAddToCart}
        />
      ))}
    </ul>
  );
}

3. Virtualization for Large Lists

import { FixedSizeList } from 'react-window';

function BigList({ items }) {
  return (
    <FixedSizeList
      height={500}
      width={300}
      itemSize={50}
      itemCount={items.length}
    >
      {({ index, style }) => (
        <div style={style}>
          Item {items[index].id}
        </div>
      )}
    </FixedSizeList>
  );
}

Testing React Components

1. Jest + React Testing Library Setup

// Button.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';

test('handles clicks', () => {
  const handleClick = jest.fn();
  render(<Button onClick={handleClick}>Click me</Button>);
  
  fireEvent.click(screen.getByText('Click me'));
  expect(handleClick).toHaveBeenCalledTimes(1);
});

2. Testing Custom Hooks

import { renderHook, act } from '@testing-library/react-hooks';
import useCounter from './useCounter';

test('increments counter', () => {
  const { result } = renderHook(() => useCounter());
  
  act(() => {
    result.current.increment();
  });
  
  expect(result.current.count).toBe(1);
});

3. Integration Test Example

test('adds item to cart', async () => {
  render(
    <CartProvider>
      <ProductList />
      <Cart />
    </CartProvider>
  );

  fireEvent.click(screen.getAllByText('Add to Cart')[0]);
  expect(screen.getByText('1 items in cart')).toBeInTheDocument();
});

Practical Example: Optimized Data Table

function DataTable({ rows }) {
  const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' });

  const sortedRows = useMemo(() => {
    if (!sortConfig.key) return rows;
    
    return [...rows].sort((a, b) => {
      if (a[sortConfig.key] < b[sortConfig.key]) {
        return sortConfig.direction === 'asc' ? -1 : 1;
      }
      if (a[sortConfig.key] > b[sortConfig.key]) {
        return sortConfig.direction === 'asc' ? 1 : -1;
      }
      return 0;
    });
  }, [rows, sortConfig]);

  const requestSort = (key) => {
    setSortConfig(prev => ({
      key,
      direction: prev.key === key && prev.direction === 'asc' ? 'desc' : 'asc'
    }));
  };

  return (
    <table>
      <thead>
        <tr>
          {columns.map(column => (
            <th key={column.key} onClick={() => requestSort(column.key)}>
              {column.label}
              {sortConfig.key === column.key && (
                sortConfig.direction === 'asc' ? ' ↑' : ' ↓'
              )}
            </th>
          ))}
        </tr>
      </thead>
      <tbody>
        {sortedRows.map(row => (
          <TableRow key={row.id} row={row} />
        ))}
      </tbody>
    </table>
  );
}

const TableRow = React.memo(function ({ row }) {
  return (
    <tr>
      {columns.map(column => (
        <td key={column.key}>{row[column.key]}</td>
      ))}
    </tr>
  );
});

What’s Next?

In our final installment, we’ll cover:
✅ State management with Redux Toolkit
✅ Server-side rendering with Next.js
✅ Deployment strategies for React apps

Exercise for You

Create an ImageLoader component that uses React.memo and implements intersection observer to lazy-load images only when they’re about to appear in the viewport.


Enjoyed this deep dive? Subscribe for our final part! 🎓

Questions? Let’s discuss in the comments!


Series Navigation:

🔹 Part 1: Introduction to React
🔹 Part 2: JSX, Components & Props
🔹 Part 3: State and Events
🔹 Part 4: useEffect & Conditional Rendering
🔹 Part 5: Custom Hooks & Context API
🔹 Part 6: Advanced Patterns & Optimization (You’re here)
🔹 Part 7: Redux & Next.js

Leave a Comment

Your email address will not be published. Required fields are marked *