React Developer Cheat Sheet (Hooks, Patterns and the useEffect Rules)
β‘ Quick Answer
A practical React cheat sheet β hooks reference, the useEffect dependency rules, state patterns, performance, and the mistakes that cause infinite render loops.
Get more content like this on Telegram!
Daily AI tips, notes & resources β free
Advertisement
React Developer Cheat Sheet
React has about eight hooks you will use regularly and one β useEffect β that causes most of the bugs.
This sheet is weighted accordingly. The hooks reference is short; the useEffect section is long, because the dependency rules are rules rather than intuition, which is exactly what belongs on a reference card.
Updated for 2026. React 19; notes where behaviour differs from React 18.
The Hooks You Actually Use
| Hook | Purpose |
|---|---|
useState | Local component state |
useEffect | Synchronise with something outside React |
useRef | A mutable value that does not trigger re-render |
useContext | Read from a context provider |
useReducer | State with complex or grouped transitions |
useMemo | Cache an expensive computed value |
useCallback | Cache a function identity |
useId | Stable unique id for accessibility attributes |
Everything else is either rare or framework-specific.
useState
const [count, setCount] = useState(0);
const [user, setUser] = useState(null);
const [items, setItems] = useState(() => expensiveInit()); // lazy init
setCount(5); // direct
setCount(c => c + 1); // functional β use when based on previous
setUser(u => ({ ...u, name: "New" })); // immutable object update
setItems(i => [...i, newItem]); // immutable array updateUse the functional form whenever the new value depends on the old one. State updates are batched, so this breaks:
setCount(count + 1);
setCount(count + 1); // both read the same stale `count` β result is +1and this works:
setCount(c => c + 1);
setCount(c => c + 1); // +2Never mutate state. items.push(x) followed by setItems(items) does nothing β the reference is unchanged, so React sees no update. Always create a new array or object.
Lazy initialisation β useState(() => expensive()) runs the function only on the first render. useState(expensive()) runs it on every render and throws the result away, which is a silent performance drain.
useEffect β The Long Section
useEffect(() => {
// effect body
return () => {
// cleanup β runs before the next effect and on unmount
};
}, [dep1, dep2]);| Dependency array | When it runs |
|---|---|
| Omitted entirely | After every render |
[] | Once, after the first render |
[a, b] | When a or b changes |
What goes in the array
Every reactive value the effect body reads β props, state, context, and anything defined during render. Not refs. Not setState functions, which React guarantees are stable.
The mental model that helps: the array is not a list of triggers. It is a list of things the effect uses, and React re-synchronises when any of them change.
Why it loops forever
Almost always because a dependency changes identity every render:
// BROKEN β new object every render β effect fires β setState β render β repeat
const options = { limit: 10 };
useEffect(() => { fetchData(options); }, [options]);Objects, arrays, and functions compare by reference. { limit: 10 } is a different object each render even though it looks identical.
Three fixes, best first:
// 1. move it inside β no longer a dependency
useEffect(() => {
const options = { limit: 10 };
fetchData(options);
}, []);
// 2. depend on a primitive instead of the object
useEffect(() => { load(user.id); }, [user.id]); // not [user]
// 3. stabilise the identity
const options = useMemo(() => ({ limit: 10 }), []);Do not silence the linter. The exhaustive-dependencies rule is right far more often than it is wrong. If you want to remove a dependency, the real problem is usually that the effect is doing something that is not synchronisation.
Cleanup
useEffect(() => {
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []);
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal }).then(setData);
return () => controller.abort(); // cancel stale requests
}, [url]);Every subscription, timer, listener, and in-flight request needs cleanup. Without it you get memory leaks and β worse β race conditions where a slow response from an old request overwrites a fast response from a new one.
What useEffect is not for
Not for data fetching in new code. Doing it correctly means hand-writing loading state, error state, race handling, cleanup, caching, and refetching. Use TanStack Query or SWR, or fetch at the framework level in Next.js.
Not for deriving state from props. Just compute it during render:
// unnecessary
const [full, setFull] = useState("");
useEffect(() => { setFull(`${first} ${last}`); }, [first, last]);
// correct
const full = `${first} ${last}`;useEffect is for synchronising with something outside React β a WebSocket, a browser API, a non-React widget.
useRef
const inputRef = useRef(null);
<input ref={inputRef} />
inputRef.current.focus();
const renderCount = useRef(0);
renderCount.current++; // mutating does NOT re-renderTwo distinct uses: reaching a DOM node, and holding a mutable value that should survive renders without causing them. Changing .current never triggers a re-render β if you need the UI to update, you need state, not a ref.
Lists and Keys
{items.map(item => (
<Item key={item.id} {...item} />
))}Use a stable id, not the array index. The key tells React which component instance corresponds to which item. Index keys match by position, so reordering, filtering, or inserting anywhere but the end attaches component state β focus, scroll, input values β to the wrong item.
Index keys are safe only when the list never reorders and items are only appended.
Performance
const Memoized = React.memo(Component); // skip re-render if props equal
const value = useMemo(() => expensive(a, b), [a, b]);
const handler = useCallback(() => doThing(id), [id]);Measure before optimising. All three have a real cost: they allocate, they compare dependencies on every render, and they add noise.
Use useMemo when a computation genuinely shows in a profiler, or when the value is passed where reference identity matters. Use useCallback when the function goes to a React.memo child or into another hook's dependencies.
React 19's compiler handles most of this automatically. In new code, manual memoisation should be the exception.
The most common performance mistake is not missing memoisation β it is defining a component inside another component:
// BROKEN β new component type every render, full remount, state lost
function Parent() {
function Child() { return <div />; }
return <Child />;
}Define components at module level. Always.
Forms
// controlled
const [value, setValue] = useState("");
<input value={value} onChange={e => setValue(e.target.value)} />
// uncontrolled β often simpler
const ref = useRef();
<input ref={ref} defaultValue="x" />For anything beyond three fields, use a form library β React Hook Form is the common choice β rather than hand-rolling validation and touched state.
Conditional Rendering
{isLoggedIn && <Dashboard />}
{isLoggedIn ? <Dashboard /> : <Login />}
{items.length > 0 && <List items={items} />}The && trap with numbers:
{items.length && <List />} // renders "0" when the array is empty0 is falsy, so && returns 0 β and React renders the number zero into your page. Compare explicitly: items.length > 0 &&.
Custom Hooks
Any function starting with use that calls other hooks:
function useDebounce(value, delay = 500) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const id = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(id);
}, [value, delay]);
return debounced;
}Custom hooks share logic, not state. Two components using useDebounce each get their own independent state.
The Five Mistakes
1. Mutating state. items.push(x) then setItems(items) does nothing. Create a new array.
2. Object or function dependencies in useEffect. New identity every render, infinite loop. Depend on primitives.
3. Array index as key. Breaks the moment the list reorders.
4. Defining a component inside a component. Full remount every render, all state lost.
5. {count && <X />} where count can be 0. Renders a literal 0. Use an explicit comparison.
Print This Section
STATE setCount(c => c + 1) when based on previous
setItems(i => [...i, x]) never mutate
EFFECT deps = everything the body reads (not refs, not setState)
infinite loop β an object/function dep β use a primitive
always clean up: intervals, listeners, fetch (AbortController)
NOT for fetching (use TanStack Query) or deriving state
KEYS stable id, never the array index
PERF measure first Β· React 19 compiler handles most memoisation
never define a component inside a component
TRAP {count && <X/>} renders "0" β {count > 0 && <X/>}Most React bugs are useEffect bugs, and most useEffect bugs are dependency-identity bugs. Learning why an object dependency loops is worth more than learning the other seven hooks combined.
π Next in this collection: CSS and Tailwind Cheat Sheet, or return to The Complete Developer Cheat Sheet Collection.
Advertisement
π¬ DiscussionPowered by GitHub Discussions
Frequently Asked Questions

AI & Software Engineering Editorial Team
The AiTechWorlds editorial team writes and reviews in-depth guides on artificial intelligence, machine learning, prompt engineering, programming, and developer tools. Every article is fact-checked against primary sources and kept up to date for working developers and CS students.
Not sure yet? Ask AI about this article
Get an instant, unbiased AI summary of βReact Developer Cheat Sheet (Hooks, Patterns and the useEffect Rules)β.
Advertisement
Related Articles
Bash Scripting Cheat Sheet (The First Line Every Script Needs)
A practical Bash scripting cheat sheet β variables, conditionals, loops, functions, and the set -euo pipefail line that turns silent failure into loud failure.
15 Coding Interview Patterns That Solve Most Problems
The 15 coding interview patterns that cover most problems β the signal that identifies each one, a minimal code skeleton, and a two-minute recognition table.
CSS and Tailwind Cheat Sheet: Flexbox, Grid and Centering Solved
A practical CSS and Tailwind cheat sheet β Flexbox vs Grid decision rules, every centering method, responsive breakpoints, and the specificity traps.
Data Structures and Big-O Cheat Sheet for Coding Interviews
A practical big O cheat sheet β the complexity ladder with real examples, data structure operation tables, and how to state your complexity in an interview.