Performance

1 min read
Rapid overview

React Performance Optimization

React.memo

// Prevents re-render if props haven't changed
const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
  return <div>{/* expensive rendering */}</div>;
});

// Custom comparison
const MemoizedComponent = React.memo(
  Component,
  (prevProps, nextProps) => {
    return prevProps.id === nextProps.id; // true = skip render
  }
);

Code Splitting

import { lazy, Suspense } from 'react';

const LazyComponent = lazy(() => import('./HeavyComponent'));

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

Virtual Lists

import { FixedSizeList } from 'react-window';

function VirtualList({ items }) {
  return (
    <FixedSizeList
      height={600}
      itemCount={items.length}
      itemSize={50}
      width="100%"
    >
      {({ index, style }) => (
        <div style={style}>{items[index]}</div>
      )}
    </FixedSizeList>
  );
}

Profiler API

import { Profiler } from 'react';

function onRenderCallback(
  id, // component id
  phase, // "mount" or "update"
  actualDuration, // time spent rendering
  baseDuration, // estimated time without memoization
  startTime,
  commitTime,
  interactions
) {
  console.log(`${id} took ${actualDuration}ms to ${phase}`);
}

function App() {
  return (
    <Profiler id="App" onRender={onRenderCallback}>
      <Router />
    </Profiler>
  );
}

Optimization Patterns

// ✅ Avoid inline object/array creation in render
function Parent() {
  const config = useMemo(() => ({ theme: 'dark' }), []);
  return <Child config={config} />;
}

// ✅ Debounce expensive operations
const debouncedSearch = useMemo(
  () => debounce((query) => search(query), 300),
  []
);

// ✅ Lazy initialize state
const [data, setData] = useState(() => expensiveComputation());

// ✅ Batch state updates
ReactDOM.unstable_batchedUpdates(() => {
  setState1(value1);
  setState2(value2);
});