Bỏ qua để vào nội dung chính
React DevTools Profiler: Find and Fix Re-renders in 5 Minutes

React DevTools Profiler: Find and Fix Re-renders in 5 Minutes

Bởi Daniel Foster
21 thg 3, 20266 phút đọc

Most React performance problems are caused by unnecessary re-renders. Learn to use the React DevTools Profiler to identify and eliminate them quickly.

Your React app feels sluggish. Typing in an input field has visible lag. Switching tabs takes 300ms. You suspect re-renders, but guessing which component is the culprit wastes hours. The React DevTools Profiler can pinpoint the exact problem in under five minutes.

Setting Up the Profiler

Install the React DevTools browser extension if you haven't already. Open your app, go to the Profiler tab in DevTools, and click the record button (blue circle). Perform the interaction that feels slow — click a button, type in a field, open a modal — then stop recording.

You'll see a flame chart showing every component that rendered during your interaction. The wider a bar, the longer it took to render. Components highlighted in yellow or red are your targets.

Reading the Flame Chart

Each bar represents a component render. Click on any bar to see:

  • Why did this render? — props changed, state changed, or parent rendered
  • Render duration — how long this specific component took
  • Commit information — total time for the entire render cycle

Enable "Record why each component rendered" in Profiler settings. This is the single most useful feature — it tells you exactly which prop or state change triggered each render.

Common Pattern: Unstable References

The most frequent cause of unnecessary re-renders is creating new object or function references on every render. Here's a classic example and its fix:

// ❌ BAD: New object reference every render
function UserList({ users }: { users: User[] }) {
  return (
    <div>
      {users.map(user => (
        <UserCard
          key={user.id}
          user={user}
          style={{ marginBottom: 16 }}  // New object every render!
          onSelect={() => selectUser(user.id)}  // New function every render!
        />
      ))}
    </div>
  );
}

// ✅ GOOD: Stable references
const cardStyle = { marginBottom: 16 };

function UserList({ users }: { users: User[] }) {
  const handleSelect = useCallback((id: string) => {
    selectUser(id);
  }, []);

  return (
    <div>
      {users.map(user => (
        <UserCard
          key={user.id}
          user={user}
          style={cardStyle}
          onSelect={() => handleSelect(user.id)}
        />
      ))}
    </div>
  );
}

Common Pattern: Context Over-Subscription

When you put frequently-changing values in React Context, every consumer re-renders on every change — even if they only read a stable part of the context. The fix is to split your contexts:

// ❌ BAD: One context with mixed update frequencies
const AppContext = createContext<{
  user: User;          // Changes rarely
  theme: Theme;        // Changes rarely
  notifications: Notification[];  // Changes every few seconds
}>();

// ✅ GOOD: Separate contexts by update frequency
const UserContext = createContext<User>();
const ThemeContext = createContext<Theme>();
const NotificationContext = createContext<Notification[]>();

// Components reading only user/theme won't re-render
// when notifications update

The useMemo / React.memo Decision Framework

Don't wrap everything in React.memo — it has its own cost (shallow comparison on every render). Use it when:

  • The component renders frequently due to parent re-renders
  • The component's own rendering is expensive (large lists, complex DOM)
  • The component receives the same props most of the time

Skip it for simple components (buttons, labels) — the memo overhead exceeds the render cost.

Quick Profiler Workflow

  1. Open Profiler, enable "Record why each component rendered"
  2. Record the slow interaction
  3. Sort the flame chart by render duration (click the ranked chart icon)
  4. Click the slowest component — check why it rendered
  5. If "parent rendered" — consider React.memo
  6. If "props changed" — check for unstable references
  7. If "state changed" — verify the state change is necessary
  8. Fix, re-profile, confirm improvement

Takeaways

  • The Profiler's "why did this render" feature is your most powerful diagnostic tool
  • Unstable object/function references cause the majority of unnecessary re-renders
  • Split contexts by update frequency to prevent cascade re-renders
  • Use React.memo selectively — only on expensive components with stable props
  • Profile first, optimize second — never guess at performance problems

Không spam, hủy đăng ký bất kỳ lúc nào.

Bài viết liên quan