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
setStatecalls 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
useCallbackto stabilize - Objects/arrays passed as props create new reference every render — wrap in
useMemo
Events
- React uses synthetic events — not native DOM events
onChangeon inputs fires on every keystroke, not on blur like native HTMLe.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 />}renders0— use ternary instead