--- trigger: glob glob: src/*tsx,src/*jsx --- | No | Category | Guideline | Description | Do | Don't | Code Good | Code Bad | Severity | Docs URL | | --- | ------------- | ----------------------------------------- | ---------------------------------------------------- | ---------------------------------------------------- | ---------------------------------------------- | ----------------------------------------------- | ------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------- | --- | | 1 | State | Use useState for local state | Simple component state should use useState hook | useState for form inputs, toggles, counters | Class components this.state | `const [count, setCount] = useState(0)` | `this.state = { count: 0 }` | Medium | [useState](https://react.dev/reference/react/useState) | | 2 | State | Lift state up when needed | Share state between siblings by lifting to parent | Lift shared state to common ancestor | Prop drilling through many levels | Parent holds state, passes down | Deep prop chains | Medium | [Sharing State](https://react.dev/learn/sharing-state-between-components) | | 3 | State | Use useReducer for complex state | Complex state logic benefits from reducer pattern | useReducer for state with multiple sub-values | Multiple useState for related values | useReducer with action types | 5+ useState calls that update together | Medium | [useReducer](https://react.dev/reference/react/useReducer) | | 4 | State | Avoid unnecessary state | Derive values from existing state when possible | Compute derived values in render | Store derivable values in state | `const total = items.reduce(...)` | `const [total, setTotal] = useState(0)` | High | [Choosing State Structure](https://react.dev/learn/choosing-the-state-structure) | | 5 | State | Initialize state lazily | Use function form for expensive initial state | useState(() => computeExpensive()) | useState(computeExpensive()) | useState(() => JSON.parse(data)) | useState(JSON.parse(data)) | Medium | [Avoiding Recreating Initial State](https://react.dev/reference/react/useState#avoiding-recreating-the-initial-state) | | 6 | Effects | Clean up effects | Return cleanup function for subscriptions, timers | Return cleanup function in useEffect | No cleanup for subscriptions | `useEffect(() => { sub(); return unsub; })` | `useEffect(() => { subscribe(); })` | High | [Connecting to External Systems](https://react.dev/reference/react/useEffect#connecting-to-an-external-system) | | 7 | Effects | Specify dependencies correctly | Include all values used inside effect in deps array | All referenced values in dependency array | Empty deps with external references | `[value] when using value in effect` | `[] when using props/state in effect` | High | [Specifying Reactive Dependencies](https://react.dev/reference/react/useEffect#specifying-reactive-dependencies) | | 8 | Effects | Avoid unnecessary effects | Don't use effects for transforming data or events | Transform data during render, handle events directly | useEffect for derived state or event handling | `const filtered = items.filter(...)` | `useEffect(() => setFiltered(items.filter(...)))` | High | [You Might Not Need an Effect](https://react.dev/learn/you-might-not-need-an-effect) | | 9 | Effects | Use refs for non-reactive values | Store values that don't trigger re-renders in refs | useRef for interval IDs, DOM elements | useState for values that don't need render | `const intervalRef = useRef(null)` | `const [intervalId, setIntervalId] = useState()` | Medium | [useRef](https://react.dev/reference/react/useRef) | | 10 | Rendering | Use keys properly | Stable unique keys for list items | Use stable IDs as keys | Array index as key for dynamic lists | `key={item.id}` | `key={index}` | High | [Keeping List Items in Order](https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key) | | 11 | Rendering | Memoize expensive calculations | Use useMemo for costly computations | useMemo for expensive filtering/sorting | Recalculate every render | `useMemo(() => expensive(), [deps])` | `const result = expensiveCalc()` | Medium | [useMemo](https://react.dev/reference/react/useMemo) | | 12 | Rendering | Memoize callbacks passed to children | Use useCallback for functions passed as props | useCallback for handlers passed to memoized children | New function reference every render | `useCallback(() => {}, [deps])` | `const handler = () => {}` | Medium | [useCallback](https://react.dev/reference/react/useCallback) | | 13 | Rendering | Use React.memo wisely | Wrap components that render often with same props | memo for pure components with stable props | memo everything or nothing | `memo(ExpensiveList)` | `memo(SimpleButton)` | Low | [React.memo](https://react.dev/reference/react/memo) | | 14 | Rendering | Avoid inline object/array creation in JSX | Create objects outside render or memoize | Define style objects outside component | Inline objects in props | `
` | `
` | Medium | | | 15 | Components | Keep components small and focused | Single responsibility for each component | One concern per component | Large multi-purpose components | `` | `` with 500 lines | Medium | | | 16 | Components | Use composition over inheritance | Compose components using children and props | Use children prop for flexibility | Inheritance hierarchies | `{content}` | `class SpecialCard extends Card` | Medium | [Thinking in React](https://react.dev/learn/thinking-in-react) | | 17 | Components | Colocate related code | Keep related components and hooks together | Related files in same directory | Flat structure with many files | `components/User/UserCard.tsx` | `components/UserCard.tsx` + `hooks/useUser.ts` | Low | | | 18 | Components | Use fragments to avoid extra DOM | Fragment or <> for multiple elements without wrapper | <> for grouping without DOM node | Extra div wrappers | `<>` for grouping without DOM node | `
{items.map(...)}
` | Low | [React.Fragment](https://react.dev/reference/react/Fragment) | | 19 | Props | Destructure props | Destructure props for cleaner component code | Destructure in function signature | props.name props.value throughout | `function User({ name, age })` | `function User(props)` | Low | | | 20 | Props | Provide default props values | Use default parameters or defaultProps | Default values in destructuring | Undefined checks throughout | `function Button({ size = 'md' })` | `if (size === undefined) size = 'md'` | Low | | | 21 | Props | Avoid prop drilling | Use context or composition for deeply nested data | Context for global data, composition for UI | Passing props through 5+ levels | `` | `` | Medium | [Passing Data Deeply](https://react.dev/learn/passing-data-deeply-with-context) | | 22 | Props | Validate props with TypeScript | Use TypeScript interfaces for prop types | interface Props { name: string } | PropTypes or no validation | `interface ButtonProps { onClick: () => void }` | `Button.propTypes = {}` | Medium | | | 23 | Events | Use synthetic events correctly | React normalizes events across browsers | e.preventDefault() e.stopPropagation() | Access native event unnecessarily | `onClick={(e) => e.preventDefault()}` | `onClick={(e) => e.nativeEvent.preventDefault()}` | Low | [React Event Object](https://react.dev/reference/react-dom/components/common#react-event-object) | | 24 | Events | Avoid binding in render | Use arrow functions in class or hooks | Arrow functions in functional components | bind in render or constructor | `const handleClick = () => {}` | `this.handleClick.bind(this)` | Medium | | | 25 | Events | Pass event handlers not call results | Pass function reference not invocation | onClick={handleClick} | onClick={handleClick()} causing immediate call | `onClick={handleClick}` | `onClick={handleClick()}` | High | | | 26 | Forms | Controlled components for forms | Use state to control form inputs | value + onChange for inputs | Uncontrolled inputs with refs | `` | `` | Medium | [Controlling an Input](https://react.dev/reference/react-dom/components/input#controlling-an-input-with-a-state-variable) | | 27 | Forms | Handle form submission properly | Prevent default and handle in submit handler | onSubmit with preventDefault | onClick on submit button only | `
` | `