Skip to main content
These rules help you maintain clean, maintainable React code by catching architectural anti-patterns.

Rules

Severity: warn
Rule ID: react-doctor/no-generic-handler-names
Requires descriptive event handler names. Names like handleClick don’t describe what the handler does, making code harder to understand.Why it’s bad:
  • handleClick doesn’t explain the action
  • Multiple handlers in a component become confusing
  • Reduces code readability
Bad:
<button onClick={handleClick}>Submit</button>
<button onClick={handleChange}>Update</button>
Good:
<button onClick={handleSubmit}>Submit</button>
<button onClick={handleUpdateProfile}>Update</button>
Generic suffixes detected: Click, Change, Input, Blur, Focus.
Severity: warn
Rule ID: react-doctor/no-giant-component
Flags components over 300 lines. Large components are hard to understand, test, and maintain.Why it’s bad:
  • Difficult to understand at a glance
  • Hard to test in isolation
  • Often violates single responsibility principle
  • Makes refactoring risky
Bad:
function Dashboard() {
  // 500 lines of code...
}
Good:
function Dashboard() {
  return (
    <>
      <DashboardHeader />
      <DashboardMetrics />
      <DashboardTable />
      <DashboardFooter />
    </>
  );
}
Consider breaking large components into:
  • Smaller sub-components
  • Custom hooks for logic
  • Utility functions for calculations
Severity: warn
Rule ID: react-doctor/no-render-in-render
Prevents calling render functions inline (e.g., renderHeader() in JSX). This breaks React’s reconciliation and causes unnecessary re-renders.Why it’s bad:
  • React can’t track component identity
  • State is lost on every render
  • Poor performance
  • Can’t use hooks in render functions
Bad:
function Page() {
  const renderHeader = () => <Header />;
  
  return <div>{renderHeader()}</div>;
}
Good:
function Page() {
  return (
    <div>
      <Header />
    </div>
  );
}
Detects function names matching /^render[A-Z]/.
Severity: error
Rule ID: react-doctor/no-nested-component-definition
Prevents defining components inside other components. This creates a new component instance on every render, destroying all state.Why it’s bad:
  • Component recreated on every parent render
  • All state is lost
  • Props change even if values are the same
  • Performance impact
Bad:
function Parent() {
  const [count, setCount] = useState(0);
  
  function Child() {
    // This is recreated every time Parent renders!
    return <div>Child</div>;
  }
  
  return <Child />;
}
Good:
function Child() {
  return <div>Child</div>;
}

function Parent() {
  const [count, setCount] = useState(0);
  return <Child />;
}
If you need to pass parent state, use props:
function Child({ count }) {
  return <div>{count}</div>;
}

function Parent() {
  const [count, setCount] = useState(0);
  return <Child count={count} />;
}

Best Practices

Component Size

Keep components focused and small:
  • Extract complex logic into custom hooks
  • Break UI into smaller sub-components
  • Use composition over giant components

Component Organization

// ✅ Good file structure
components/
  Dashboard/
    Dashboard.tsx       // Main component
    DashboardHeader.tsx // Sub-components
    DashboardMetrics.tsx
    useDashboard.ts     // Custom hook
    types.ts            // Types

Naming Conventions

  • Components: PascalCase (UserProfile)
  • Handlers: descriptive names (handleSubmitForm, handleDeleteUser)
  • Hooks: start with use (useAuth, useDashboard)

Build docs developers (and LLMs) love