devnotes.

Performance: memo, useMemo, useCallback & Code Splitting

Most React apps don't need manual optimization — React is fast by default. Reach for these tools only after you've actually noticed a performance problem (use the React DevTools Profiler to confirm it), not preemptively.

React.memo — skip re-rendering when props haven't changed

const StudentCard = React.memo(function StudentCard({ name, cgpa }) {
  console.log('rendering', name);
  return <div>{name} — {cgpa}</div>;
});

memo does a shallow comparison of props; if none changed, React reuses the previous render output and skips re-rendering that subtree.

useMemo — cache an expensive calculation

function Dashboard({ students }) {
  const topStudents = useMemo(
    () => students.filter(s => s.cgpa >= 3.5).sort((a, b) => b.cgpa - a.cgpa),
    [students] // only recompute when `students` changes
  );

  return <StudentList students={topStudents} />;
}

useCallback — cache a function reference

Without useCallback, a new function is created on every render, which can defeat React.memo on a child that receives it as a prop:

function ParentComponent() {
  const [count, setCount] = useState(0);

  // stable reference across renders unless dependencies change
  const handleSave = useCallback((id) => {
    console.log('saving', id);
  }, []);

  return <ExpensiveChild onSave={handleSave} />;
}

const ExpensiveChild = React.memo(function ExpensiveChild({ onSave }) {
  // only re-renders when onSave (or other props) actually changes
  return <button onClick={() => onSave(1)}>Save</button>;
});

Code splitting with lazy and Suspense

Load large components (routes, modals, charts) only when they're actually needed, shrinking the initial bundle:

import { lazy, Suspense } from 'react';

const AnalyticsChart = lazy(() => import('./AnalyticsChart'));

function Dashboard() {
  return (
    <Suspense fallback={<p>Loading chart...</p>}>
      <AnalyticsChart />
    </Suspense>
  );
}

List virtualization

For lists with hundreds or thousands of rows, render only the visible ones using a library like TanStack Virtual or react-window instead of rendering the entire list at once.

A quick checklist before optimizing

  1. Profile first — don't guess where the slowness is.
  2. Move state as close as possible to where it's used, so unrelated components don't re-render.
  3. Use the children prop pattern to keep expensive subtrees out of a re-rendering parent.
  4. Only then reach for memo / useMemo / useCallback on the components that are actually shown to be slow.
React Performance Optimization Techniques | DevNotes