React Rules to Remember

Rendering

  • A component re-renders when state or props change
  • Re-rendering a parent re-renders all children unless wrapped in React.memo
  • Re-renders are not the same as DOM updates — React diffs first
  • State updates are asynchronous — you won't see new value immediately after setState

State

  • Never mutate state directly — always return new reference
  • Objects and arrays need to be spread: setState({ ...state, key: value })
  • Multiple setState calls in one event handler are batched into one re-render
  • State updates from the same value do not trigger re-render

useEffect

  • Empty array [] — runs once on mount
  • No array — runs after every render
  • With deps — runs when any dep changes
  • Always clean up subscriptions, timers, and event listeners in the return function
  • Objects and functions as deps cause infinite loops unless stabilized

Keys

  • Keys must be unique among siblings
  • Never use array index as key if list can reorder or filter
  • Changing a key unmounts and remounts the component completely

Props

  • Props are read-only — never mutate them
  • Functions passed as props create new reference every render — wrap in useCallback to stabilize
  • Objects/arrays passed as props create new reference every render — wrap in useMemo

Events

  • React uses synthetic events — not native DOM events
  • onChange on inputs fires on every keystroke, not on blur like native HTML
  • e.preventDefault() works the same as native

Forms

  • Controlled input: value driven by state, every change goes through onChange
  • Uncontrolled input: value lives in DOM, read via ref
  • Prefer controlled — you have full visibility into form state at all times

Hooks

  • Only call hooks at the top level — never inside loops, conditions, or nested functions
  • Only call hooks inside React function components or custom hooks
  • Custom hooks must start with use

Common Mistakes

  • Reading state immediately after setting it — it won't be updated yet
  • Missing cleanup in useEffect causing memory leaks
  • Stale closure — event handler or timer capturing old state value instead of current
  • Forgetting dependency array on useEffect causes it to run on every render
  • Using && with a number: {0 && <Component />} renders 0 — use ternary instead
Show Comments