REACT Lesson 22 – Performance Optimization | Dataplexa
LESSON 22

Performance Optimization

Speed up React apps with memo, useMemo, useCallback, and component optimization techniques that make DataFlow dashboard blazing fast.

React performance matters. A slow dashboard frustrates users. DataFlow needs to handle thousands of transactions, real-time updates, and complex charts without lag. Performance optimization isn't premature optimization. It's about understanding how React works and preventing wasteful re-renders.

Why React Apps Get Slow

React re-renders components when state or props change. But sometimes components re-render unnecessarily. Every re-render means JavaScript work, DOM updates, and potential lag. Common performance killers: - Parent components re-rendering all children - Expensive calculations running on every render - New objects created in JSX causing prop changes - Large lists without proper keys - Context updates triggering widespread re-renders The DataFlow dashboard has 50+ components. If the header re-renders, it shouldn't force the entire data table to re-render too.

React.memo - Preventing Unnecessary Re-renders

React.memo wraps components and prevents re-renders when props haven't changed. Think of it like a smart bouncer that only lets through actual changes.
// DataFlow stats card without memo - re-renders always
function StatsCard({ title, value, icon }) {
  console.log('StatsCard rendered:', title);
  return (
    <div style={{ padding: '20px', background: '#fff', borderRadius: '8px' }}>
      <h3>{title}</h3>
      <p style={{ fontSize: '24px', fontWeight: 'bold' }}>{value}</p>
    </div>
  );
}
DataFlow Dashboard
Now wrap it with React.memo:
// Optimized with React.memo
const StatsCard = React.memo(function StatsCard({ title, value, icon }) {
  console.log('StatsCard rendered:', title);
  return (
    <div style={{ padding: '20px', background: '#fff', borderRadius: '8px' }}>
      <h3>{title}</h3>
      <p style={{ fontSize: '24px', fontWeight: 'bold' }}>{value}</p>
    </div>
  );
});
DataFlow Dashboard

What just happened?

React.memo compared the props and skipped re-rendering because title and value didn't change. Click the button multiple times - the console stays quiet. Try this: Change a prop value and watch the component re-render only then.

useMemo - Caching Expensive Calculations

useMemo caches the result of expensive calculations. Only recalculates when dependencies change. Perfect for DataFlow's transaction processing.
// DataFlow transaction summary - expensive calculation
function TransactionSummary({ transactions }) {
  // Expensive calculation - runs every render
  const summary = transactions.reduce((acc, transaction) => {
    acc.total += transaction.amount;
    acc.count += 1;
    if (transaction.type === 'sale') acc.sales += transaction.amount;
    return acc;
  }, { total: 0, count: 0, sales: 0 });

  return (
    <div>
      <h3>Transaction Summary</h3>
      <p>Total: ${summary.total.toLocaleString()}</p>
      <p>Count: {summary.count}</p>
    </div>
  );
}
DataFlow Dashboard
Now with useMemo:
// Optimized with useMemo
function TransactionSummary({ transactions }) {
  // Only recalculate when transactions array changes
  const summary = React.useMemo(() => {
    console.log('Calculating summary...');
    return transactions.reduce((acc, transaction) => {
      acc.total += transaction.amount;
      acc.count += 1;
      if (transaction.type === 'sale') acc.sales += transaction.amount;
      return acc;
    }, { total: 0, count: 0, sales: 0 });
  }, [transactions]);

  return (
    <div>
      <h3>Transaction Summary</h3>
      <p>Total: ${summary.total.toLocaleString()}</p>
    </div>
  );
}
DataFlow Dashboard

What just happened?

useMemo cached the calculation result. React only recalculates when the transactions dependency changes. Multiple re-renders use the cached value. Try this: Notice how the console message only appears once, even when clicking the button multiple times.

useCallback - Preventing Function Recreation

useCallback caches function definitions. Without it, every render creates new function objects, breaking memo comparisons and causing child re-renders.
// DataFlow filter component - function created every render
function FilteredTransactions({ transactions }) {
  const [filter, setFilter] = React.useState('all');
  
  // New function every render - breaks React.memo
  const handleFilterChange = (newFilter) => {
    setFilter(newFilter);
  };

  const filteredData = transactions.filter(tx => 
    filter === 'all' ? true : tx.type === filter
  );

  return (
    <div>
      <FilterButtons onFilterChange={handleFilterChange} />
      <TransactionList transactions={filteredData} />
    </div>
  );
}
DataFlow Dashboard
Fixed with useCallback:
// Optimized with useCallback
function FilteredTransactions({ transactions }) {
  const [filter, setFilter] = React.useState('all');
  
  // Same function reference across renders
  const handleFilterChange = React.useCallback((newFilter) => {
    setFilter(newFilter);
  }, []); // No dependencies - function never changes

  const filteredData = React.useMemo(() => 
    transactions.filter(tx => 
      filter === 'all' ? true : tx.type === filter
    ), [transactions, filter]
  );

  return (
    <div>
      <FilterButtons onFilterChange={handleFilterChange} />
      <TransactionList transactions={filteredData} />
    </div>
  );
}
DataFlow Dashboard

What just happened?

useCallback returned the same function reference each time, allowing React.memo to work properly. The memoized child component didn't re-render unnecessarily. Try this: Click the filter buttons to see they still work while avoiding pointless re-renders.

Component Optimization Strategies

Real apps need multiple optimization techniques working together. Here's how DataFlow's transaction table stays fast with 10,000+ rows:

Virtual Scrolling

Only render visible rows. Libraries like react-window handle thousands of items smoothly.

Debounced Search

Wait 300ms after typing stops before filtering. Prevents excessive API calls and renders.

Lazy Components

Load heavy chart components only when needed. React.lazy + Suspense handle code splitting.

Smart Keys

Use stable, unique keys for list items. Helps React efficiently update and reorder elements.

Common Performance Pitfalls

Avoid these mistakes that kill React performance:

Creating Objects in JSX

style={{padding: '20px'}} creates new objects every render. Move to CSS or useMemo for expensive styles.

Index as Key

key={index} breaks React's reconciliation when items reorder. Use stable IDs instead.

Overusing Context

Context updates re-render all consumers. Split contexts by update frequency and use selectors.

Performance optimization is about measurement, not guessing. Use React DevTools Profiler to find actual bottlenecks. Don't optimize everything upfront - measure first, optimize what matters. Netflix reduced their Time to Interactive by 50% just by implementing proper code splitting. Airbnb's search stays responsive with thousands of listings through virtualization. These techniques work at scale. Remember: Fast apps feel better, convert better, and users actually want to use them. DataFlow dashboard users expect instant responses when filtering 50,000 transactions. Performance isn't optional.

Quiz

1. The DataFlow stats component re-renders every time the parent updates, even though its revenue prop stays the same. What optimization should you use?


2. DataFlow processes 10,000 transactions on every render to calculate totals, even when the data hasn't changed. Which hook optimizes this?


3. A DataFlow filter component wrapped in React.memo still re-renders when its parent updates, even though the onFilter prop function does the same thing. Why?


Up Next: Code Splitting

Break large bundles into smaller chunks that load on demand - keep DataFlow fast even with massive feature sets.