13 KiB
13 KiB
| 1 | No | Category | Guideline | Description | Do | Don't | Code Good | Code Bad | Severity | Docs URL |
|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 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 | https://react.dev/reference/react/useState |
| 3 | 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 | https://react.dev/learn/sharing-state-between-components |
| 4 | 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 | https://react.dev/reference/react/useReducer |
| 5 | 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 | https://react.dev/learn/choosing-the-state-structure |
| 6 | 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 | https://react.dev/reference/react/useState#avoiding-recreating-the-initial-state |
| 7 | 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 | https://react.dev/reference/react/useEffect#connecting-to-an-external-system |
| 8 | 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 | https://react.dev/reference/react/useEffect#specifying-reactive-dependencies |
| 9 | 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 | https://react.dev/learn/you-might-not-need-an-effect |
| 10 | 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 | https://react.dev/reference/react/useRef |
| 11 | 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 | https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key |
| 12 | 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 | https://react.dev/reference/react/useMemo |
| 13 | 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 | https://react.dev/reference/react/useCallback |
| 14 | 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 | https://react.dev/reference/react/memo |
| 15 | 14 | Rendering | Avoid inline object/array creation in JSX | Create objects outside render or memoize | Define style objects outside component | Inline objects in props | <div style={styles.container}> | <div style={{ margin: 10 }}> | Medium | |
| 16 | 15 | Components | Keep components small and focused | Single responsibility for each component | One concern per component | Large multi-purpose components | <UserAvatar /><UserName /> | <UserCard /> with 500 lines | Medium | |
| 17 | 16 | Components | Use composition over inheritance | Compose components using children and props | Use children prop for flexibility | Inheritance hierarchies | <Card>{content}</Card> | class SpecialCard extends Card | Medium | https://react.dev/learn/thinking-in-react |
| 18 | 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 | |
| 19 | 18 | Components | Use fragments to avoid extra DOM | Fragment or <> for multiple elements without wrapper | <> for grouping without DOM node | Extra div wrappers | <>{items.map(...)}</> | <div>{items.map(...)}</div> | Low | https://react.dev/reference/react/Fragment |
| 20 | 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 | |
| 21 | 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 | |
| 22 | 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 | <UserContext.Provider> | <A user={u}><B user={u}><C user={u}> | Medium | https://react.dev/learn/passing-data-deeply-with-context |
| 23 | 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 | |
| 24 | 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 | https://react.dev/reference/react-dom/components/common#react-event-object |
| 25 | 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 | |
| 26 | 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 | |
| 27 | 26 | Forms | Controlled components for forms | Use state to control form inputs | value + onChange for inputs | Uncontrolled inputs with refs | <input value={val} onChange={setVal}> | <input ref={inputRef}> | Medium | https://react.dev/reference/react-dom/components/input#controlling-an-input-with-a-state-variable |
| 28 | 27 | Forms | Handle form submission properly | Prevent default and handle in submit handler | onSubmit with preventDefault | onClick on submit button only | <form onSubmit={handleSubmit}> | <button onClick={handleSubmit}> | Medium | |
| 29 | 28 | Forms | Debounce rapid input changes | Debounce search/filter inputs | useDeferredValue or debounce for search | Filter on every keystroke | useDeferredValue(searchTerm) | useEffect filtering on every change | Medium | https://react.dev/reference/react/useDeferredValue |
| 30 | 29 | Hooks | Follow rules of hooks | Only call hooks at top level and in React functions | Hooks at component top level | Hooks in conditions loops or callbacks | const [x, setX] = useState() | if (cond) { const [x, setX] = useState() } | High | https://react.dev/reference/rules/rules-of-hooks |
| 31 | 30 | Hooks | Custom hooks for reusable logic | Extract shared stateful logic to custom hooks | useCustomHook for reusable patterns | Duplicate hook logic across components | const { data } = useFetch(url) | Duplicate useEffect/useState in components | Medium | https://react.dev/learn/reusing-logic-with-custom-hooks |
| 32 | 31 | Hooks | Name custom hooks with use prefix | Custom hooks must start with use | useFetch useForm useAuth | fetchData or getData for hook | function useFetch(url) | function fetchData(url) | High | |
| 33 | 32 | Context | Use context for global data | Context for theme auth locale | Context for app-wide state | Context for frequently changing data | <ThemeContext.Provider> | Context for form field values | Medium | https://react.dev/learn/passing-data-deeply-with-context |
| 34 | 33 | Context | Split contexts by concern | Separate contexts for different domains | ThemeContext + AuthContext | One giant AppContext | <ThemeProvider><AuthProvider> | <AppProvider value={{theme user...}}> | Medium | |
| 35 | 34 | Context | Memoize context values | Prevent unnecessary re-renders with useMemo | useMemo for context value object | New object reference every render | value={useMemo(() => ({...}), [])} | value={{ user, theme }} | High | |
| 36 | 35 | Performance | Use React DevTools Profiler | Profile to identify performance bottlenecks | Profile before optimizing | Optimize without measuring | React DevTools Profiler | Guessing at bottlenecks | Medium | https://react.dev/learn/react-developer-tools |
| 37 | 36 | Performance | Lazy load components | Use React.lazy for code splitting | lazy() for routes and heavy components | Import everything upfront | const Page = lazy(() => import('./Page')) | import Page from './Page' | Medium | https://react.dev/reference/react/lazy |
| 38 | 37 | Performance | Virtualize long lists | Use windowing for lists over 100 items | react-window or react-virtual | Render thousands of DOM nodes | <VirtualizedList items={items}/> | {items.map(i => <Item />)} | High | |
| 39 | 38 | Performance | Batch state updates | React 18 auto-batches but be aware | Let React batch related updates | Manual batching with flushSync | setA(1); setB(2); // batched | flushSync(() => setA(1)) | Low | https://react.dev/learn/queueing-a-series-of-state-updates |
| 40 | 39 | ErrorHandling | Use error boundaries | Catch JavaScript errors in component tree | ErrorBoundary wrapping sections | Let errors crash entire app | <ErrorBoundary><App/></ErrorBoundary> | No error handling | High | https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary |
| 41 | 40 | ErrorHandling | Handle async errors | Catch errors in async operations | try/catch in async handlers | Unhandled promise rejections | try { await fetch() } catch(e) {} | await fetch() // no catch | High | |
| 42 | 41 | Testing | Test behavior not implementation | Test what user sees and does | Test renders and interactions | Test internal state or methods | expect(screen.getByText('Hello')) | expect(component.state.name) | Medium | https://testing-library.com/docs/react-testing-library/intro/ |
| 43 | 42 | Testing | Use testing-library queries | Use accessible queries | getByRole getByLabelText | getByTestId for everything | getByRole('button') | getByTestId('submit-btn') | Medium | https://testing-library.com/docs/queries/about#priority |
| 44 | 43 | Accessibility | Use semantic HTML | Proper HTML elements for their purpose | button for clicks nav for navigation | div with onClick for buttons | <button onClick={...}> | <div onClick={...}> | High | https://react.dev/reference/react-dom/components#all-html-components |
| 45 | 44 | Accessibility | Manage focus properly | Handle focus for modals dialogs | Focus trap in modals return focus on close | No focus management | useEffect to focus input | Modal without focus trap | High | |
| 46 | 45 | Accessibility | Announce dynamic content | Use ARIA live regions for updates | aria-live for dynamic updates | Silent updates to screen readers | <div aria-live="polite">{msg}</div> | <div>{msg}</div> | Medium | |
| 47 | 46 | Accessibility | Label form controls | Associate labels with inputs | htmlFor matching input id | Placeholder as only label | <label htmlFor="email">Email</label> | <input placeholder="Email"/> | High | |
| 48 | 47 | TypeScript | Type component props | Define interfaces for all props | interface Props with all prop types | any or missing types | interface Props { name: string } | function Component(props: any) | High | |
| 49 | 48 | TypeScript | Type state properly | Provide types for useState | useState<Type>() for complex state | Inferred any types | useState<User | null>(null) | useState(null) | Medium | |
| 50 | 49 | TypeScript | Type event handlers | Use React event types | React.ChangeEvent<HTMLInputElement> | Generic Event type | onChange: React.ChangeEvent<HTMLInputElement> | onChange: Event | Medium | |
| 51 | 50 | TypeScript | Use generics for reusable components | Generic components for flexible typing | Generic props for list components | Union types for flexibility | <List<T> items={T[]}> | <List items={any[]}> | Medium | |
| 52 | 51 | Patterns | Container/Presentational split | Separate data logic from UI | Container fetches presentational renders | Mixed data and UI in one | <UserContainer><UserView/></UserContainer> | <User /> with fetch and render | Low | |
| 53 | 52 | Patterns | Render props for flexibility | Share code via render prop pattern | Render prop for customizable rendering | Duplicate logic across components | <DataFetcher render={data => ...}/> | Copy paste fetch logic | Low | https://react.dev/reference/react/cloneElement#passing-data-with-a-render-prop |
| 54 | 53 | Patterns | Compound components | Related components sharing state | Tab + TabPanel sharing context | Prop drilling between related | <Tabs><Tab/><TabPanel/></Tabs> | <Tabs tabs={[]} panels={[...]}/> | Low |